this關鍵字的理解與總結
1.this關鍵字的含義
簡單說,this
就是屬性或方法“當前”所在的物件。
this
都有一個共同點:它總是返回一個物件。
舉例:
var A = { name: '張三', describe: function () { return '姓名:'+ this.name; } }; A.describe(); // '姓名:張三'
由於物件的屬性可以賦給另一個物件,所以屬性所在的當前物件是可變的,即this
的指向是可變的。
var A = { name: '張三', describe: function () { return '姓名:'+ this.name; } }; var B = { name: '李四' }; B.describe = A.describe; B.describe() // "姓名:李四"
重構如下
function f() { return '姓名:'+ this.name; } var A = { name: '張三', describe: f }; var B = { name: '李四', describe: f }; A.describe() // "姓名:張三" B.describe() // "姓名:李四"
只要函式被賦給另一個變數,this
的指向就會變。
var A = { name: '張三', describe: function () { return '姓名:'+ this.name; } }; var name = '李四'; var f = A.describe; f() // "姓名:李四"
A.describe
被賦值給變數f
,內部的this
就會指向f
執行時所在的物件(本例是頂層物件);
一切皆物件,執行環境也是物件,所以函式都是在某個物件之中執行,this
就是函式執行時所在的物件(環境)。
2.this的實質
var obj = { foo: 5 };
原始的物件以字典結構儲存;
引擎先從obj
拿到記憶體地址,然後再從該地址讀出原始的物件,返回它的foo
屬性。
{ foo: { [[value]]: 5 [[writable]]: true [[enumerable]]: true [[configurable]]: true } }
屬性值是一個函式的情況
var obj = { foo: function () {} };
{
foo: {
[[value]]: 函式的地址
...
}
}
由於函式是一個單獨的值,所以它可以在不同的環境(上下文)執行。
它的設計目的就是在函式體內部,指代函式當前的執行環境。
3.使用場合
(1)全域性環境
全域性環境使用this
,它指的就是頂層物件window
。
this === window // true
(2)建構函式
建構函式中的this
,指的是例項物件。
var Obj = function (p) { this.p = p; };
var o = new Obj('Hello World!'); o.p // "Hello World!"
(3)物件的方法
如果物件的方法裡面包含this
,this
的指向就是方法執行時所在的物件。該方法賦值給另一個物件,就會改變this
的指向。
如果this
所在的方法不在物件的第一層,這時this
只是指向當前一層的物件,而不會繼承更上面的層。
var a = { p: 'Hello', b: { m: function() { console.log(this.p); } } }; a.b.m() // undefined
因此,實際執行是下面這個程式碼
var b = { m: function() { console.log(this.p); } }; var a = { p: 'Hello', b: b }; (a.b).m() // 等同於 b.m()
想要達到預期效果是
var a = { b: { m: function() { console.log(this.p); }, p: 'Hello' } };
如果這時將巢狀物件內部的方法賦值給一個變數,this
依然會指向全域性物件。
var a = { b: { m: function() { console.log(this.p); }, p: 'Hello' } }; var hello = a.b.m; hello() // undefined
4. 1)避免多層this
var o = { f1: function () { console.log(this); var f2 = function () { console.log(this); }(); } } o.f1() // Object // Window
實際執行如下
var temp = function () { console.log(this); }; var o = { f1: function () { console.log(this); var f2 = temp(); } }
一個解決方法是在第二層改用一個指向外層this
的變數。
that
,固定指向外層的this
,然後在內層使用that
,就不會發生this
指向的改變。
var o = { f1: function() { console.log(this); var that = this; var f2 = function() { console.log(that); }(); } } o.f1() // Object // Object
4. 2)避免陣列處理方法中的this
var o = { v: 'hello', p: [ 'a1', 'a2' ], f: function f() { this.p.forEach(function (item) { console.log(this.v + ' ' + item); }); } } o.f() // undefined a1 // undefined a2
解決方法
1.使用中間變數固定this;
2.另一種方法是將this
當作foreach
方法的第二個引數,固定它的執行環境。
var o = { v: 'hello', p: [ 'a1', 'a2' ], f: function f() { this.p.forEach(function (item) { console.log(this.v + ' ' + item); }, this); } } o.f() // hello a1 // hello a2
3.避免在回撥函式中使用this;
1.Function.prototype.call()
call
方法的引數,應該是一個物件。如果引數為空、null
和undefined
,則預設傳入全域性物件。
2.Function.prototype.apply()
3.Function.prototype.bind()
var counter = { count: 0, inc: function () { this.count++; } }; var func = counter.inc.bind(counter); func(); counter.count // 1
如果bind
方法的第一個引數是null
或undefined
,等於將this
繫結到全域性物件,函式執行時this
指向頂層物件(瀏覽器為window
);
function add(x, y) { return x + y; } var plus5 = add.bind(null, 5); plus5(10) // 15