JavaScript中的this關鍵字的幾種用法
JS 裡的 this
- 在 function 內部被建立
- 指向呼叫時所在函式所繫結的物件(拗口)
- this 不能被賦值,但可以被 call/apply 改變
1. this 和建構函式
function C(){ this.a = 37; } var o = new C(); console.log(o.a); // logs 37 function C2(){ this.a = 37; return {a:38}; } var b = new C2(); console.log(b.a); // logs 38
2. this 和物件
物件內部方法的this指向呼叫這些方法的物件:
- 函式的定義位置不影響其this指向,this指向只和呼叫函式的物件有關。
- 多層巢狀的物件,內部方法的this指向離被呼叫函式最近的物件(window也是物件,其內部物件呼叫方法的this指向內部物件, 而非window)。
//1:this指向呼叫函式的物件 var o = { prop: 37, f: function() { return this.prop; } }; console.log(o.f()); //37 this指向o var a = o.f; console.log(a()): //undefined this指向a ,a中沒有定義prop var o = {prop: 37}; function independent() { return this.prop; } o.f = independent; console.log(o.f()); // logs 37 this指向o
//2:this指向離被呼叫函式最近的物件 var o = { prop: 37, f: function() { return this.prop; } }; function independent() { return this.prop; } o.b = { g: independent, prop: 42 }; console.log(o.b.g()); //42 this指向o.b
3. this 和函式
普通函式內部的this分兩種情況,嚴格模式和非嚴格模式。
//非嚴格模式下,this 預設指向全域性物件window
function f1(){
return this;
}
f1() === window; // true
//而嚴格模式下, this為undefined
function f2(){
"use strict"; // 這裡是嚴格模式
return this;
}
f2() === undefined; // true
4. 全域性環境的this
前面提到 this 是 “指向呼叫時所在函式所繫結的物件”, 這句話拗口但絕對正確,沒有一個多餘的字。
全域性環境中有不同的宿主物件,瀏覽器環境中是 window, node 環境中是 global。這裡重點說下瀏覽器環境中的 this。
瀏覽器環境中非函式內 this 指向 window
alert(window=== this) // true
因此你會看很很多開源 JS lib 這麼寫
(function() {
// ...
})(this);
或這樣寫
(function() {
// ...
}).call(this);
比如 underscore 和 requirejs,大意是把全域性變數 window 傳入匿名函式內快取起來,避免直接訪問。至於為啥要快取,這跟 JS 作用域鏈有關係,讀取越外層的識別符號效能會越差。
瀏覽器中比較坑人,非函式內直接使用 var 宣告的變數預設為全域性變數,且預設掛在 window 上作為屬性。
var andy = '劉德華'
alert(andy === window.andy) // true
alert(andy === this.andy) // true
alert(window.andy === this.andy) // true
因為這個特性,有些筆試題如
var x = 10;
function func() {
alert(this.x)
}
var obj = {
x: 20,
fn: function() {
alert(this.x)
}
}
var fn = obj.fn
func() // 10
fn() // 10
沒錯,最終輸出的都是全域性的 10。永遠記住這一點:
判斷 this 指向誰,看執行時而非定義時,只要函式(function)沒有繫結在物件上呼叫,它的 this 就是 window。
5. this和DOM事件
當函式被當做監聽事件處理函式時, 其 this 指向觸發該事件的元素 (針對於addEventListener事件)
// 被呼叫時,將關聯的元素變成藍色
function bluify(e){
//在控制檯打印出所點選元素
console.log(this);
//阻止時間冒泡
e.stopPropagation();
//阻止元素的預設事件
e.preventDefault();
this.style.backgroundColor = '#A5D9F3';
}
// 獲取文件中的所有元素的列表
var elements = document.getElementsByTagName('*');
// 將bluify作為元素的點選監聽函式,當元素被點選時,就會變成藍色
for(var i=0 ; i<elements.length ; i++){
elements[i].addEventListener('click', bluify, false);
6. this和內聯事件
內聯事件中的this指向分兩種情況:
- 當代碼被內聯處理函式呼叫時,它的this指向監聽器所在的DOM元素
當代碼被包括在函式內部執行時,其this指向等同於 函式直接呼叫的情況,即在非嚴格模式指向全域性物件window, 在嚴格模式指向undefined
依次點選上邊的三個按鈕後在控制檯的輸出結果7. setTimeout & setInterval
對於延時函式內部的回撥函式的this指向全域性物件window(當然我們可以通過bind方法改變其內部函式的this指向)
看下邊程式碼及截圖
//預設情況下程式碼
function Person() {
this.age = 0;
setTimeout(function() {
console.log(this);
}, 3000);
}
var p = new Person();//3秒後返回 window 物件
==============================================
//通過bind繫結
function Person() {
this.age = 0;
setTimeout((function() {
console.log(this);
}).bind(this), 3000);
}
var p = new Person();//3秒後返回建構函式新生成的物件 Person{...}
8. this可以被 call/apply 改變
當函式通過Function物件的原型中繼承的方法 call() 和 apply() 方法呼叫時, 其函式內部的this值可繫結到 call() & apply() 方法指定的第一個物件上, 如果第一個引數不是物件,JavaScript內部會嘗試將其轉換成物件然後指向它。
例子:
function add(c, d){
return this.a + this.b + c + d;
}
var o = {a:1, b:3};
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
function tt() {
console.log(this);
}
// 返回物件見下圖(圖1)
tt.call(5); // Number {[[PrimitiveValue]]: 5}
tt.call('asd'); // String {0: "a", 1: "s", 2: "d", length: 3, [[PrimitiveValue]]: "asd"}
9. me/self/that/_this 暫存 this
如果採用 OOP 方式寫 JS 程式碼,無可避免的會用到 this,方法內會訪問類的內部屬性(欄位),也可能會呼叫類的另一個方法。當類的方法內又有一個 function 時,比如瀏覽器端開發經常遇見的給 DOM 元素新增事件,這時如果事件處理器(handler)中的想呼叫類的一個方法,此時 handler 內的 this 是 dom 元素而非類的當前物件。這個時候,需要把 this 暫存,開發者發揮著自己的聰明才智留下了幾種經典的命名** me, self, that, _this**。如
如:
一般會在每個方法的第一句就把 this 暫存下來。
10. ES5 中新增的 bind 和 this
bind方法在ES5引入, 在Function的原型鏈上, Function.prototype.bind。通過bind方法繫結後, 函式將被永遠繫結在其第一個引數物件上, 而無論其在什麼情況下被呼叫。
function f(){
return this.a;
}
var g = f.bind({a:"azerty"});
console.log(g()); // azerty
var o = {a:37, f:f, g:g};
console.log(o.f(), o.g()); // 37, azerty
11. ES6 箭頭函式=> 和 this
箭頭函式的一個重要特徵就是顛覆了上面的一句話,再貼一次
判斷 this 指向誰,看執行時而非定義時,只要函式(function)沒有繫結在物件上呼叫,它的 this 就是 window
是的,前面一直用這句話來判斷 this 的指向,在箭頭函式裡前面半句就失效了。箭頭函式的特徵就是,定義在哪,this 就指向那。即箭頭函式定義在一個物件裡,那箭頭函式裡的 this 就指向該物件。如下
var book = {
author: 'John Resig',
init: function() {
document.onclick = ev => {
alert(this.author) ; // 這裡的 this 不是 document 了
}
}
};
book.init()
物件 book 裡有一個屬性 author, 有一個 init 方法, 給 document 添加了一個點選事件,如果是傳統的函式,我們知道 this 指向應該是 document,但箭頭函式會指向當前物件 book。
箭頭函式讓 JS 迴歸自然和簡單,函式定義在哪它 this 就指向哪,定義在物件裡它指向該物件,定義在類的原型上,就指向該類的例項,望文知意這樣更容易理解。
作為方法的箭頭函式this指向全域性window物件,而普通函式則指向呼叫它的物件
原文參考:
https://www.cnblogs.com/snandy/p/4773184.html
http://www.cnblogs.com/dongcanliang/p/7054176.html