該筆記參考自:《你不知道的JavaScript上卷》
一.關(guān)于this
1.this是一個很特別的關(guān)鍵字,被自動定義在所有函數(shù)的作用域中。
2.為什么要使用this?
①編寫一個函數(shù),將一個字符串中的字符全部轉(zhuǎn)化為大寫的形式,如:"yyc"--->"YYC"。
function convertToUpperCase(s){
return s.toUpperCase();
}
var name = 'yyc';
convertToUpperCase(name);
>>>"YYC"
②但是,在實(shí)際情況下往往不會單獨(dú)聲明一個變量來僅僅表示一個人的名字,往往將一個人用一個對象表示,而他/她的名字僅代表是該對象的一個屬性。
function convertToUpperCase(obj){
return obj.name.toUpperCase();
}
var person1 = {
name: 'yyc'
};
convertToUpperCase(person1);
>>>"YYC"
//多個人
function convertToUpperCase(context){
return context.name.toUpperCase();
}
var person1 = {
name: 'yyc'
};
var person2 = {
name: 'asan'
};
var person3 = {
name: 'tuhao'
};
convertToUpperCase(person1);
>>>"YYC"
convertToUpperCase(person2);
>>>"ASAN"
convertToUpperCase(person3);
"TUHAO"
③現(xiàn)在增加一個功能:來yyc和大家打個招呼,同時將你的名字全部用大寫表示喔。
function convertToUpperCase(context){
return context.name.toUpperCase();
}
function speak(context){
var greeting = "Hello,l'm " + convertToUpperCase(context);
return greeting;
}
var person1 = {
name: 'yyc'
};
speak(person1);
>>>"Hello,l'm YYC"
④現(xiàn)在可以引入this了,說好聽點(diǎn),讓代碼變得更加優(yōu)雅。說白了,就是可以偷懶了。
function convertToUpperCase(){
return this.name.toUpperCase();
}
function speak(){
var greeting = "Hello,l'm " + convertToUpperCase.call(this);
return greeting;
}
var person1 = {
name: 'yyc'
};
var person = {
name: 'asan'
};
convertToUpperCase.call(person1);
>>>"YYC"
convertToUpperCase.call(person2);
>>>"ASAN"
speak.call(person1);
>>>"Hello,l'm YYC"
speak.call(person2);
>>>"Hello,l'm ASAN"
3.誤解之一:this指向函數(shù)自身
function foo(num){
console.log('foo: ' + num);
this.count++;
}
foo.count = 0;
for(var i = 0;i < 10;i++){
if(i > 5){
foo(i);
}
}
console.log(foo.count);
>>>
foo: 6
foo: 7
foo: 8
foo: 9
0
①輸出結(jié)果表明foo()函數(shù)的確被調(diào)用了4次,但是foo.count仍然為0,證明this不是指向函數(shù)foo的。
②那么foo()函數(shù)中的this到底指向什么呢?
- 全局對象
③為什么是全局對象呢?
- 因?yàn)?code>foo()函數(shù)的調(diào)用類型是:獨(dú)立函數(shù)調(diào)用,也就是默認(rèn)綁定。
④那怎么判斷是默認(rèn)綁定呢?
- 在代碼中,
foo()是直接使用不帶任何修飾的函數(shù)引用進(jìn)行調(diào)用的,因此只能使用默認(rèn)綁定,無法應(yīng)用其他規(guī)則。
⑤現(xiàn)在讓我們看一下全局對象中的count變量的值吧?
count;
>>>NaN
⑥為什么是NaN?
undefined++;
>>>NaN
⑦啥意思?
- 因?yàn)楫?dāng)
foo()函數(shù)被獨(dú)立調(diào)用時,執(zhí)行this.count++;語句,而全局對象中并沒有count這個屬性,由于LHS查詢,全局對象好心創(chuàng)建了一個屬性,但是該屬性的初始值為undefined。
4.解決方法:
①看到紅燈,右轉(zhuǎn)。
function foo(num){
data.count++;
}
var data = {
count: 0
};
for(var i = 0;i < 10;i++){
if(i > 5){
foo(i);
}
}
console.log(data.count);
>>>4
②通過具名函數(shù)名,從函數(shù)內(nèi)部引用它自身:
function foo(num){
foo.count++;
}
foo.count = 0;
for(var i = 0;i < 10;i++){
if(i > 5){
foo(i);
}
}
console.log(foo.count);
>>>4
③非要用this?,那就強(qiáng)制把this指向foo()函數(shù):
function foo(num){
this.count++;
}
foo.count = 0;
for(var i = 0;i < 10;i++){
if(i > 5){
foo.call(foo,i);
}
}
console.log(foo.count);
>>>4
5.作用域---回爐一下
①作用域是根據(jù)名稱查找變量的一套規(guī)則。
②LHS查詢和RHS查詢分別是什么?
LHS查詢:Left Hand Side,試圖找到某個變量的容器本身,從而對其賦值。
RHS查詢:Retrieve His Source,僅僅找到某個變量的值。
③那么如何區(qū)分這兩種查詢?
//LHS查詢----針對變量b
function foo(a){
b = a + 1;
return b;
}
foo(2);
>>>3
- 第一次對變量
b進(jìn)行LHS查詢時,并沒有在foo()函數(shù)中找到其聲明,同時在全局作用域中仍未找到該變量的聲明,因此全局作用域會創(chuàng)建一個具有該名稱的變量。
b;
>>>3
//RHS----針對變量b
function foo(a){
return a+b;
}
foo(2);
>>>Uncaught ReferenceError: b is not defined
- 此時,對變量
b進(jìn)行的是RHS查詢,因?yàn)閮H需要知道該變量的值即可,不需要對其賦值。不過,同上,因?yàn)樵?code>foo()和全局作用域中均沒有對變量b的聲明,這個時候,沒有就是沒有,返回ReferenceError!
④小結(jié)
- 不成功的
RHS引用會導(dǎo)致拋出ReferenceError異常。不成功的LHS引用會導(dǎo)致自動隱式地創(chuàng)建一個全局變量(非嚴(yán)格模式下),該變量使用LHS引用的目標(biāo)作為標(biāo)識符,或者拋出ReferenceError異常(嚴(yán)格模式下)。
6.this誤解之二:指向其函數(shù)的詞法作用域
function foo(){
var a = 2;
bar();
}
function bar(){
console.log(this.a);
}
foo();
>>>undefined
①因?yàn)檩敵鰹?code>undefined,而不是2,證明this并不是指向其函數(shù)的作用域。
②需要明確一點(diǎn):this在任何情況下都不會指向函數(shù)的詞法作用域。
③還有個小問題?為什么上面的輸出是undefined,而不是ReferenceError?
- 當(dāng)一個變量進(jìn)行
RHS查詢,如果在任何作用域中都沒有找個變量的聲明,則輸出ReferenceError異常! - 但當(dāng)你查找對象中的一個屬性時,若該屬性不存在,則返回
undefined:
var obj = {};
obj.a;
>>>undefined
- 之前的代碼片段中
foo()函數(shù)內(nèi)部的this指向全局對象,內(nèi)部沒有a屬性,因此返回undefined.