this關鍵詞
參考:阮一峰
this沒有作用域的限制,巢狀函式不會從呼叫它的函式中繼承this。如果巢狀函式作為方法呼叫this,this指向呼叫它的物件,如果巢狀函式作為函式呼叫,this值不是全域性物件就是undefined。
this總是返回
一個物件,this
就是屬性或方法“當前”所在的物件。
一:由於物件的屬性可以賦給另一個物件,所以屬性所在的當前物件是可變的,即this
的指向是可變的
var A = {
name: '張三',
describe: function () {
return '姓名:'+ this.name;
}
};
var B = {
name: '李四'
};
B.describe = A.describe;
B.describe()
// "姓名:李四"
複製程式碼
二:只要函式被賦給另一個變數,this
的指向就會變。A.describe
被賦值給變數f
,內部的this
就會指向f
執行時所在的物件(本例是頂層物件)。
var A = {
name: '張三',
describe: function () {
return '姓名:'+ this.name;
}
};
var name = '李四';
var f = A.describe;
f() // "姓名:李四"
複製程式碼
JavaScript 語言之中,一切皆物件,執行環境也是物件,所以函式都是在某個物件之中執行,this
就是函式執行時所在的物件(環境)。這本來並不會讓使用者糊塗,但是 JavaScript 支援執行環境動態切換,也就是說,this
的指向是動態的,沒有辦法事先確定到底指向哪個物件。
使用場合:
一:全域性環境
this === window // true
function f() {
console.log(this === window);
}
f() // true
複製程式碼
二:建構函式:指向例項物件
var Obj = function (p) {
this.p = p;
};
var o = new Obj('Hello World!');
o.p // "Hello World!"
複製程式碼
三:物件的方法
如果物件的方法裡面包含this
,this
的指向就是方法執行時所在的物件。該方法賦值給另一個物件,就會改變this
的指向。
var obj ={
foo: function () {
console.log(this);
}
};
obj.foo() // obj
複製程式碼
但是,下面這幾種用法,都會改變this
的指向。
// 情況一
(obj.foo = obj.foo)() // window
// 情況二
(false || obj.foo)() // window
// 情況三
(1, obj.foo)() // window
複製程式碼
上面程式碼中,obj.foo
就是一個值。這個值真正呼叫的時候,執行環境已經不是obj
了,而是全域性環境,所以this
不再指向obj
。
可以這樣理解,JavaScript 引擎內部,obj
和obj.foo
儲存在兩個記憶體地址,稱為地址一和地址二。obj.foo()
這樣呼叫時,是從地址一呼叫地址二,因此地址二的執行環境是地址一,this
指向obj
。但是,上面三種情況,都是直接取出地址二進行呼叫,這樣的話,執行環境就是全域性環境,因此this
指向全域性環境。上面三種情況等同於下面的程式碼。
// 情況一
(obj.foo = function () {
console.log(this);
})()
// 等同於
(function () {
console.log(this);
})()
// 情況二
(false || function () {
console.log(this);
})()
// 情況三
(1, function () {
console.log(this);
})()
複製程式碼
如果this
所在的方法不在物件的第一層,這時this
只是指向當前一層的物件,而不會繼承更上面的層。
var a = {
b: {
m: function() {
console.log(this.p);
},
p: 'Hello'
}
};
var hello = a.b.m;
hello() // undefined
複製程式碼
上面程式碼中,m
是多層物件內部的一個方法。為求簡便,將其賦值給hello
變數,結果呼叫時,this
指向了頂層物件。為了避免這個問題,可以只將m
所在的物件賦值給hello
,這樣呼叫時,this
的指向就不會變。
var hello = a.b;
hello.m() // Hello
複製程式碼
使用注意點:
一:避免多層this
var o = {
f1: function() {
console.log(this);
var that = this;
var f2 = function() {
console.log(that);
}();
}
}
o.f1()
// Object
// Object
複製程式碼
二:避免陣列處理方法中的this
陣列的map
和foreach
方法,允許提供一個函式作為引數。這個函式內部不應該使用this
。
var o = {
v: 'hello',
p: [ 'a1', 'a2' ],
f: function f() {
var that = this;
this.p.forEach(function (item) {
console.log(that.v+' '+item);
});
}
}
o.f()
// hello a1
// hello a2
複製程式碼
三:避免回掉函式中的this
var o = new Object();
o.f = function () {
函式例項的call方法,可以指定函式內部this的指向(即函式執行時所在的作用域),然後在所指定的作用域中,呼叫該函式。 console.log(this === o);
}
// jQuery 的寫法
$('#button').on('click', o.f);
複製程式碼
控制檯會顯示false
。原因是此時this
不再指向o
物件,而是指向按鈕的 DOM 物件,因為f
方法是在按鈕物件的環境中被呼叫的。
繫結this 的方法
JavaScript 提供了call
、apply
、bind
這三個方法,來切換/固定this
的指向。
一:Function.prototype.call()
函式例項的call
方法,可以指定函式內部this
的指向(即函式執行時所在的作用域),然後在所指定的作用域中,呼叫該函式。
var obj = {};
var f = function () {
return this;
};
f() === window // true
f.call(obj) === obj // true
複製程式碼
call
方法可以改變this
的指向,指定this
指向物件obj
,然後在物件obj
的作用域中執行函式f
。call
方法的引數,應該是一個物件。如果引數為空、null
和undefined
,則預設傳入全域性物件.
func.call(thisValue, arg1, arg2, ...)
複製程式碼
二:Function.prototype.apply()
apply
方法的第一個引數也是this
所要指向的那個物件,如果設為null
或undefined
,則等同於指定全域性物件。第二個引數則是一個陣列,該陣列的所有成員依次作為引數,傳入原函式。原函式的引數,在call
方法中必須一個個新增,但是在apply
方法中,必須以陣列形式新增。
三:Function.prototype.bind()
var counter = {
count: 0,
inc: function () {
this.count++;
}
};
var func = counter.inc.bind(counter);
func();
counter.count // 1
複製程式碼
程式碼中,counter.inc
方法被賦值給變數func
。這時必須用bind
方法將inc
內部的this
,繫結到counter.
bind
方法,可以改寫一些 JavaScript 原生方法的使用形式。- bind 是返回對應函式,便於稍後呼叫;apply 、call 則是立即呼叫 。