JavaScript的this關(guān)鍵字的理解
概念性的概述this
當一個函數(shù)創(chuàng)建后,一個關(guān)鍵字this就隨之(在后臺)創(chuàng)建,它鏈接到一個對象,而函數(shù)正是在這個對象中進行操作。換句話說,關(guān)鍵字this可在函數(shù)中使用,是對一個對象的引用,而函數(shù)正是該對象的屬性或方法。
讓我們來看這個對象:
<!DOCTYPE html><html lang="en"><body><script>
var cody = {
living:true,
age:23,
gender:'male',
getGender:function(){return cody.gender;}
};
console.log(cody.getGender()); // logs 'male'
</script></body></html>
注意在函數(shù)getGender里,由于在cody對象內(nèi)部,我們可以通過.來獲取gender屬性(也就是cody.gender)。也可以用this來獲取cody對象,因為this正是指向cody對象。
<!DOCTYPE html><html lang="en"><body><script>
var cody = {
living:true,
age:23,
gender:'male',
getGender:function(){return this.gender;}
};
console.log(cody.getGender()); // logs 'male'
</script></body></html>
this.gender中this指向cody對象,而getGender函數(shù)可以操作cody對象。
關(guān)于this的主題可能有點讓人感到困惑,其實不必如此。僅記住,通常,this指向的對象正是包含函數(shù)的對象,而不是函數(shù)本身(當然也有例外,例如采用關(guān)鍵字new或者call()和apply())。
重要提示
- 關(guān)鍵字this就像其他的變量,唯一不同就是你不能更改它。
- 不同于傳給函數(shù)的其他參數(shù)和變量,在調(diào)用函數(shù)的對象中,this是一個關(guān)鍵字(而不是屬性)。
如何確定this的值?
this傳遞給所有的函數(shù),它的值取決于函數(shù)運行時何時被調(diào)用。這里請注意,因為這是你需要記住的一個很特別的地方。
下面的代碼中myObject對象有個屬性sayFoo,它指向函數(shù)sayFoo。當在全局域中調(diào)用sayFoo函數(shù)時,this指向window對象。當myObject調(diào)用函數(shù)時,this指向的是myObject。
因為myObject有個叫foo的屬性,在這里被使用。
<!DOCTYPE html><html lang="en"><body><script>
var foo = 'foo';
var myObject = {foo: 'I am myObject.foo'};
var sayFoo = function() {
console.log(this['foo']);
};
// give myObject a sayFoo property and have it point to sayFoo function
myObject.sayFoo = sayFoo;
myObject.sayFoo(); // logs 'I am myObject.foo' 12
sayFoo(); // logs 'foo'
</script></body></html>
很清楚,this的值取決于函數(shù)什么時候被調(diào)用。myObject.sayFoo和sayFoo都指向同樣的函數(shù),但sayFoo()調(diào)用的上下文不同,this的值也就不同。下面是類似的代碼,head對象(window)顯式使用,希望對你有用。
<!DOCTYPE html><html lang="en"><body><script>
window.foo = 'foo';
window.myObject = {foo: 'I am myObject.foo'};
window.sayFoo = function() { ! console.log(this.foo); };
window.myObject.sayFoo = window.sayFoo;
window.myObject.sayFoo();
window.sayFoo();
</script></body></html>
確保當你有多個引用指向同一個函數(shù)的時候,你清楚的知道this的值是隨調(diào)用函數(shù)的上下文的不同而改變。
重要提示
- 除了this以外的所有變量和參數(shù)都屬于靜態(tài)變量范圍(lexical scope)。
在嵌入函數(shù)內(nèi)this指向head對象
你可能想知道在嵌入在另外一個函數(shù)的函數(shù)中使用this會發(fā)生什么事。不幸的是在ECMA 3中,this不遵循規(guī)律,它不指向函數(shù)屬于的對象,而是指向head對象(瀏覽器的window對象)。
在下面的代碼,func2和func3中的this不再指向myObject,而是head對象。
<!DOCTYPE html><html lang="en"><body><script>
var myObject = {
func1:function() {
console.log(this); //logs myObject
varfunc2=function() {
console.log(this); //logs window, and will do so from this point on
varfunc3=function() {
console.log(this); //logs window, as it's the head object
}();
}();
}
};
myObject.func1();
</script></body></html>
然而在ECMAScript 5中,這個問題將會得到修正?,F(xiàn)在,你應(yīng)該意識到這個問題,尤其是當你將一個函數(shù)的值傳遞到另一個函數(shù)時。
看看下面的代碼,將一個匿名函數(shù)傳給foo.func1,當在foo.func1中調(diào)用匿名函數(shù)(函數(shù)嵌套在另一個函數(shù)中),匿名函數(shù)中this將會指向是head對象。
<!DOCTYPE html><html lang="en"><body><script>
var foo = {
func1:function(bar){
bar(); //logs window, not foo
console.log(this);//the this keyword here will be a reference to foo object
}
};
foo.func1(function(){console.log(this)});
</script></body></html>
現(xiàn)在你不會忘了,如果包含this的函數(shù)在另一個函數(shù)中,或者被另一個函數(shù)調(diào)用,this的值將會指向的是head對象(再說一次,這將在ECMAScript 5中被修正。)
解決嵌套函數(shù)的問題
為了使this的值不丟失,你可以在父函數(shù)中使用一個作用域鏈(scope chain)來保存對this進行引用。下面的代碼中,使用一個叫that的變量,利用它的作用域,我們可以更好的保存函數(shù)上下文。
<!DOCTYPE html><html lang="en"><body><script>
var myObject = {
myProperty:'Icanseethelight',
myMethod:function() {
var that=this; //store a reference to this (i.e.myObject) in myMethod scope varhelperFunctionfunction(){//childfunction
var helperFunction function() { //childfunction
//logs 'I can see the light' via scope chain because that=this
console.log(that.myProperty); //logs 'I can see the light'
console.log(this); // logs window object, if we don't use "that"
}();
}
}
myObject.myMethod(); // invoke myMethod
</script></body></html>
控制this的值
this的值通常取決于調(diào)用函數(shù)的上下文(除非使用關(guān)鍵字new,稍后會為你介紹),但是你可以用apply()或call()指定觸發(fā)一個函數(shù)時this指向的對象,以改變/控制this的值。用這兩種方法就好像再說:“嘿,調(diào)用X函數(shù),但讓Z對象來作this的值?!边@樣做,JavaScript默認的this的值將被更改。
下面,我們創(chuàng)建了一個對象和一個函數(shù),然后我們通過call()來觸發(fā)函數(shù),所以函數(shù)中的this指向的是myOjbect。在myFunction函數(shù)中的this會操作myObject而不是head對象,這樣我們就改變了在myFunction中this指向的對象。
<!DOCTYPE html><html lang="en"><body><script>
var myObject = {};
var myFunction = function(param1, param2) {
//setviacall()'this'points to my Object when function is invoked
this.foo = param1;
this.bar = param2;
console.log(this); //logs Object{foo = 'foo', bar = 'bar'}
};
myFunction.call(myObject, 'foo', 'bar'); // invoke function, set this value to myObject
console.log(myObject) // logs Object {foo = 'foo', bar = 'bar'}
</script></body></html>
在上面的例子,我們用了call(),apply()也可適用于同樣用法,二者的不同之處在于參數(shù)如何傳給函數(shù)。用call(),參數(shù)用逗號分開,而用apply(),參數(shù)放在一個數(shù)組中傳遞。下面是同樣的代碼,但是用apply()。
<!DOCTYPE html><html lang="en"><body><script>
var myObject = {};
var myFunction = function(param1, param2) {
//set via apply(), this points to my Object when function is invoked
this.foo=param1;
this.bar=param2;
console.log(this); // logs Object{foo='foo', bar='bar'}
};
myFunction.apply(myObject, ['foo', 'bar']); // invoke function, set this value
console.log(myObject); // logs Object {foo = 'foo', bar = 'bar'}
</script></body></html>
在自定義構(gòu)造函數(shù)中用this
當函數(shù)用關(guān)鍵字new來觸發(fā),this的值–由于在構(gòu)造函數(shù)中聲明–指向?qū)嵗旧?。換種說法:在構(gòu)造函數(shù)中,我們可以在對象真正創(chuàng)建之前,就用this來指定對象。這樣看來,this值的更改和call()或apply()相似。
下面,我們構(gòu)造了一個構(gòu)造函數(shù)Person,this指向創(chuàng)建的對象。當Person的對象創(chuàng)建后,this指向這個對象,并將屬性name放在對象內(nèi),值為傳給這個構(gòu)造函數(shù)的參數(shù)值(name)。
<!DOCTYPE html><html lang="en"><body><script>
var Person = function(name) {
this.name = name || 'johndoe'; // this will refer to the instanc ecreated
}
var cody = new Person('Cody Lindley'); // create an instance, based on Person constructor
console.log(cody.name); // logs 'Cody Lindley'
</script></body></html>
這樣,當用關(guān)鍵字new觸發(fā)構(gòu)造函數(shù)時,this指向“要創(chuàng)建的對象”。那么如果我們沒有用關(guān)鍵字new,this的值將會指向觸發(fā)Person的上下文——這時是head對象。讓我們來看看下面的代碼。
<!DOCTYPE html><html lang="en"><body><script>
var Person = function(name) {
this.name=name||'johndoe';
}
var cody = Person('Cody Lindley'); // notice we did not use 'new'
console.log(cody.name); // undefined, the value is actually set at window.name
console.log(window.name); // logs 'Cody Lindley'
</script></body></html>
在prototype方法內(nèi)的this指向構(gòu)造實例
當一個方法作為一個構(gòu)造函數(shù)的prototype屬性時,這個方法中的this指向觸發(fā)方法的實例。這里,我們有一個Person()的構(gòu)造函數(shù),它需要person的全名(full name),為了獲得全名(full name),我們在Person.prototype中加入了一個whatIsMyFullName方法,所有的Person實例都繼承該方法。這個方法中的this指向觸發(fā)這個方法的實例(以及它的屬性)。
下面我創(chuàng)建了兩個Person對象(cody和lisa),繼承的whatIsMyFullName方法包含的this就指向這個實例。
<!DOCTYPE html><html lang="en"><body><script>
var Person = function(x){
if(x){this.fullName = x};
};
Person.prototype.whatIsMyFullName = function() {
return this.fullName; // 'this' refers to the instance created from Person()
}
var cody = new Person('cody lindley');
var lisa = new Person('lisa lindley');
// call the inherited whatIsMyFullName method, which uses this to refer to the instance
console.log(cody.whatIsMyFullName(), lisa.whatIsMyFullName());
/* The prototype chain is still in effect, so if the instance does not have a
fullName property, it will look for it in the prototype chain.
Below, we add a fullName property to both the Person prototype and the Object
prototype. See notes. */
Object.prototype.fullName = 'John Doe';
var john = new Person(); // no argument is passed so fullName is not added to instance
console.log(john.whatIsMyFullName()); // logs 'John Doe'
</script></body></html>
在prototype對象內(nèi)的方法里使用this,this就指向?qū)嵗H绻麑嵗话瑢傩缘脑?,prototype查找便開始了。
提示
- 如果this指向的對象不包含想要查找的屬性,那么這時對于任何屬性都適用的法則在這里也適用,也就是,屬性會沿著prototype鏈(prototype chain)上“尋找”。所以在我們的例子中,如果實例中不包含fullName屬性,那么fullName就會查找Person.prototype.fullName,然后是Object.prototype.fullName。
查看更多JavaScript的語法,大家可以關(guān)注:《JavaScript 參考教程》、《JavaScript代碼風格指南》,也希望大家多多支持腳本之家。
相關(guān)文章
利用javascript數(shù)組長度循環(huán)數(shù)組內(nèi)所有元素
javascript循環(huán)數(shù)組內(nèi)所有元素代碼學習,大家參考使用吧2013-12-12

