今天一個朋友轉給我一道題,讓我幫忙解釋解釋。前端
當我看到題目的時候,第一眼以爲賊簡單,可是看提問越到後面越懵逼了,在琢磨着能不能猜對了…………web
var age = 10;
var person={
age:20,
getAge(){
var age = 30;
return this.age;
},
};
alert(age,age*2);
person.getAge();
var b = person.getAge;
b();
(person.getAge)();
(1,person.getAge)();
(1,person.getAge.bind(person))();
(person.getAge,person.getAge)();
(person.getAge=person.getAge)();
person.getAge.call();
person.getAge.call(person);
function getAge2() {
this.age = 40;
console.log(person.getAge());
};
getAge2();
console.log(age);
function getAge3(){
this.age = 50;
this.getAge4 = ()=>{
console.log(person.getAge.call(this));
}
}
new getAge3().getAge4();
console.log(age);
function getAge4(){
this.age = 60;
this.getAge5 = ()=>{
console.log(person.getAge.call(this));
}
}
new getAge4().getAge5();
console.log(age);
var age2 = 10;
var person2={
age2:20,
getAge2:()=>{
var age2 = 30;
return this.age2;
},
};
console.log(person2.getAge2.call());
console.log(person2.getAge2.call(person2));
複製代碼
果不其然,我答錯了好多……微信
這道題目,題簡單的不能再簡單了,就是對象,函數,變量,可是問的很深,沒有紮實的知識,很難確切的回答上這些問題。app
要回答這些問題,關鍵仍是要深刻了解 this 和 逗號表達式。編輯器
首先咱們簡單回顧下這兩個很是重要的知識。最後再看看文末的綜合題目。函數
this 在 js 中很是重要。在筆試的時候,若是有考察基礎知識,那麼出現 this 的機率那可不是通常的高。學習
一句話:誰調用就指向誰。ui
var person={
age:20,
getAge(){
var age = 30;
return this.age;
},
};
person.getAge(); // 20
複製代碼
這個的 getAge 方法是 person 調用的,因此 this 指向 person,person.age 輸出爲 20;this
一句話:調用者指向誰,則指向誰。spa
var age = 10;
var person={
age:20,
getAge:()=>{
var age = 30;
return this.age;
},
};
person.getAge(); // 10
複製代碼
這個的 getAge 方法是 person 調用的,則 getAge 和 person 的指向一致,person 是 window 調用的(參照上述普通函數),因此 person 指向 window,所以 getAge 也指向 window,輸出 10。
一句話:你說指向誰就指向誰。
改變 this 指向,有 call,apply,bind 這幾種方法。
var age = 10;
var person={
age:20,
getAge:function(){
var age = 30;
return this.age;
},
};
person.getAge.call(person);
複製代碼
這裏在執行 getAge 方法的時候,傳入了 person,那麼 getAge 的 this 指向 person,因此輸出 20。
(http://www.ecma-international.org/ecma-262/#sec-abstract-operations)
逗號表達式 能夠用於分割任何一個表達式,能夠用於分割函數參數等。
function test(){
let a=1;
return ++a,a++,a++;
}
console.log(test());
複製代碼
這裏逗號用於分割表達式,等價於:
function test(){
let a=1;
++a;
a++;
return a++;
}
console.log(test());
複製代碼
天然,答案不用多說,是 3 ,由於 return 後面的是 a++,若是是 ++a ,那結果是 4(這裏不太明白的,自行學習 「++運算符」)。
接下來咱們來一一解答上面的問題,我再貼一下題目。
var age = 10;
var person={
age:20,
getAge(){
var age = 30;
return this.age;
},
};
複製代碼
這裏逗號分隔的是參數列表(Argument Lists),由於 alert 只接受一個參數,即第一個參數,後面的都忽略。所以彈窗是 10。
這個輸出結果是 10,主要是要區別 person.getAge(),差異在於當前是先賦值給一個變量 b,而後執行 b()。
等價於:
var b = person.getAge;
window.b();
複製代碼
回到最前面的知識點,[誰調用,指向誰],這裏賦值變量以後,調用 b 方法的是 window,因此 this 指向 window,答案是 10。
這裏括號只是起到一個分割的做用,並無實際意義,等價於 person.getAge()。因此答案是 20;
這裏和上一個題目的差異是引入了逗號表達式。咱們知道逗號表達式返回的是最後一個值,即 person.getAge,注意這裏是表達式返回值。
等價於:
var a = (1,person.getAge);
a();
複製代碼
或者:
var a = (false||person.getAge);
a();
複製代碼
顯然,這裏 a 調用方是 window,因此答案是 10。注意這裏是非嚴格模式下。
後面題目若是牽涉到模式區別的時候一般都是指非嚴格模式,非嚴格模式與嚴格模式下的重要區別是當 this 爲 null 或者 undefined 的時候,是否會改成指向 window。 非嚴格模式下會改成指向 window,嚴格模式就仍然爲保持 null 或者 undefined。
"use strict";
var age = 10;
var person={
age:20,
getAge(){
var age = 30;
return this.age;
},
};
console.log(person.getAge());// 輸出仍然是 20,this 指針指向 person
var a = (1,person.getAge);
a();// Cannot read property 'age' of undefined , this 是 undefined
複製代碼
這道題是 逗號表達式 和 bind 知識點,參照上面的分析,等價於:
var a =(1,person.getAge.bind(person));
a();
複製代碼
這裏與上一道的區別是,返回的是一個 bind 以後的函數,a 方法已經強制指向 person 了,因此等價於:
person.getAge.bind(person)()
複製代碼
答案爲 20,this 指向 person。
這個其實就是一個逗號表達式,返回最後一個項的值,這裏連續設置兩個 person.getAge,只是一個陷阱,前面的 person.getAge 對結果沒影響。咬定青山山不放鬆,堅決排除陷阱。
等價於:
(1,person.getAge)();
複製代碼
答案與上面一致,爲 10。
這裏有點不同,意思爲:對象 person,給它設置了一個屬性 getAge(若是有 getAge 屬性,則從新賦值),將這個屬性 getAge 用 person.getAge 賦值。
括號裏面是一個賦值表達式,表達式的返回值,就是這個從新被賦值了 person.getAge 的 person 對象下面的 getAge 屬性。
等價於:
var person.getAge = person.getAge // 賦值
var a = person.getAge; // 表達式返回值
a();
複製代碼
上面已經解釋,因此輸出爲 10。
這裏使用 call 函數改變 this 指針。在不傳做用域參數的時候,在嚴格模式下 this 是 undefined,這裏是非嚴格模式。
call,apply,bind 均可以改變 this 指針,以下:
// 10 call 爲空,this 爲 undefined,從新指向爲 window
person.getAge.call();
// 20 this 指針指向 person
person.getAge.call(person);
複製代碼
function getAge2() {
this.age = 40;
console.log(person.getAge());// 20
};
getAge2();
console.log(age);// 40
複製代碼
裏面的 person.getAge 仍然是誰調用執行誰,person.getAge 方法的 this 是指向 person,輸出是 20。 可是裏面有一個 this.age = 40 的賦值。
那麼這個 this 是啥?固然也是誰調用指向誰,這裏的 getAge2 是 window 調用,因此這裏的 this 是 window。
那麼這至關於將外層的 age 已經修改成 40 了,因此 console.log(age) ,已經變成 40 了。
function getAge3(){
this.age = 50;
this.getAge4 = ()=>{
console.log(person.getAge.call(this));// 50
}
}
new getAge3().getAge4();
console.log(age); // 40
複製代碼
在 getAge3 裏面,定義了一個公有方法 getAge4,可是這裏是一個箭頭函數,裏面使用 call 修改函數 person.getAge 的 this 指針指向當前的 this,那麼當前 this 指向哪裏呢?
仍然是看誰調用了 getAge4,這裏是 new getAge3() 這個實例調用了 getAge4,因此 call 傳進去的 this 是指向 new getAge3(),這裏 this.age=50 被賦值爲 50 了,因此輸出爲 50。
可是全局的 age 並無被修改,與 題1 不同,這裏的 this 指向了實例對象,並非指向 window,因此全局的 age 仍然爲 40。
當讀到這裏的時候,你或許已經豁然開朗,以爲很理解了,那麼這一題你會作嗎?
var age2 = 10;
var person2={
age2:20,
getAge2:()=>{
var age2 = 30;
return this.age2;
},
};
console.log(person2.getAge2.call()); // 10
console.log(person2.getAge2.call(person2)); // 10
複製代碼
這裏的核心差異是 getAge2 函數是使用了箭頭函數。按上面理解的,箭頭函數的 this 指向 person2 ?
錯了!不是如此。
其實是箭頭函數沒有本身的 this,既然沒有,那 call 怎麼能改變它自身的 this 指針呢?
參照文檔:(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions)
因此上面不論是 call(),仍是 call(person2),都沒法修改 this 指針,因此二者都輸出全局的 10。
咱們再來看一道題,箭頭函數的 this 。
var age = 10;
var person={
age:20,
child:{
age:40,
getAge:function(){
return this.age;
},
},
child2:{
age:40,
getAge:()=>{
return this.age;
},
},
child3:function(){
this.getAge = ()=>{
return this.age;
}
}
};
console.log(person.child.getAge());// 40
console.log(person.child2.getAge()); // 10
console.log((new person.child3()).getAge()); // undfined
複製代碼
根據咱們上面的知識,能夠知道 person.child.getAge() 的 this 就是誰調用指向誰,這裏是 child 調用,因此指向 child ,輸出 40 。
咱們知道,箭頭函數是沒有本身的 this,那麼它的 this 是啥呢?
就是當前箭頭函數逐級向上查找,找到函數做用域的 this,則爲當前箭頭函數的 this。
因此,這裏 person.child2.getAge 函數的父級調用方是 child2,可是 child2 是對象,也沒有本身的 this 和 做用域,因此繼續向上查找 person,而後發現 person 也是對象,再繼續向上查找,找到 window 這個大 Boss 了,因此 this 就指向 window ,輸出爲 10。
(new person.child3()).getAge() 的 this ,同理向上一級查找,發現 new person.child3() 是個函數實例,因此 this 指向 child3 的這個實例,然而 child3 實例沒有 age 屬性,因此輸出 undefined。
這些題,確實很難!若是有不理解的,多看幾篇解答,相信你必定會豁然開朗!或者關注公衆號,加做者微信,一對一解答。
歡迎關注個人微信公衆號,一塊兒作靠譜前端!