觀感度:🌟🌟🌟🌟🌟
口味:清爽綠豆
烹飪時間:15min
你皮任你皮,我當你瓜皮。
衆所周知,this
在JavaScript中的指向一直很難讓人理解,想要學好JavaScript,this
也是我們必須要搞清楚的。其實,this
並沒有那麼難,本文將力爭帶你治療this
的“皮”。
首先,來科普三個問題!
this是什麼?
this
是聲明函數時附加的參數,指向特定的對象,也就是隱藏參數。
比如LOL中各種隱藏的彩蛋,當水晶先鋒斯卡納在草叢裏停留超過15秒不動,會模仿寵物小精靈。
“皮卡!皮卡!皮卡丘!”
“斯卡!斯卡!斯卡納!”
好,相信大家已經理解什麼是this
了,就是個隱藏參數,沒有多麼的神奇,其實 每個函數都可以訪問this
。
爲什麼要使用this?
this
提供了一種更加優雅的方式來隱式的傳遞對象引用。
通俗地說:就是說我們可以把API設計的更加簡潔而且易於複用。
說人話:那就是this
可以幫我們省略參數。
this的指向
只需要理解並記住一句話,外加幾種小情況,大家就可以完完全全的理解this
。
好,注意聽講。
這句話就是“this
的指向在函數聲明的時候是不會被確定的,只有函數執行的時候才被確定,this
最終指向的是調用它的對象”。
有人說這也太長了,記不住。
好,那縮短點。
一句話。
“this
的指向決定於函數的調用方式”。
總結:
1.this
是聲明函數時附加的參數,指向特定的對象,也就是隱藏參數。2.
this
可以幫我們省略參數。3.
this
的指向決定於函數的調用方式。
是不是很簡單,搞清楚了這三點,我們下面將從八種情況來徹底理解this
的指向問題。
直接上代碼。
一、直到世界盡頭
對,沒錯,有沒有想起灌籃高手主題曲
我們首先要了解一下世界觀,分爲三種情況。
1.在非嚴格模式下,瀏覽器中的盡頭當然就是window
。2.在嚴格模式下也就是開啓了
"use strict"
的情況下,盡頭就是undefined
。3.
node
的全局環境中盡頭是global
。
下文中情況主要從第一種非嚴格模式下來對this
的指向進行解釋和說明。
// 我們來看下面的兩個🌰。
function demo(){
var user = “前端食堂”;
console.log(this.user); // undefined
console.log(this); // window
}
demo();
// 這裏的函數demo實際上是被window對象使用點語法所點出來的,如下:
function demo(){
var user = “前端食堂”;
console.log(this.user); // undefined
console.log(this); // window
}
window.demo();
// 可以發現和上面的代碼結果一樣
二、畫龍點睛
var obj = {
user:”前端食堂”,
fn:function(){
console.log(this.user); // 前端食堂
}
}
obj.fn();
// 這裏的this指向對象obj
// 注意看最後一行調用函數的代碼
// obj.fn();
// 重要的事情說兩遍!!
// this的指向在函數創建時是決定不了的
// 在調用的時候纔可以決定,誰調用就指向誰
// this的指向在函數創建時是決定不了的
// 在調用的時候纔可以決定,誰調用就指向誰
三、點石成金
// 其實以上兩點說的還不夠準確,我們接着往下看
var obj = {
user:”前端食堂”,
fn:function(){
console.log(this.user); // 前端食堂
}
}
window.obj.fn();
// 這段代碼跟上面的代碼幾乎是一樣的
// 但是這裏爲什麼沒有指向window呢?
// 按你上面說的不是window調用的方法嗎?
// 大家先停下來打個debugger思考一下爲什麼
// 想不明白沒關係我們帶着疑問來看下段代碼
var obj = {
a:1,
b:{
a:2,
fn:function(){
console.log(this.a); // 2
}
}
}
obj.b.fn();
// 這裏執行的時候同樣是對象obj通過點語法進行的執行
// 但是this同樣沒有指向window,這是爲什麼呢?
// 好,我們有幾種情況需要記住:
// 1.如果一個函數中有this
// 但是它沒有被上一級的對象所調用
// 那麼this就會指向window(非嚴格模式下)
// 2.如果一個函數中有this
// 這個函數又被上一級的對象所調用
// 那麼this就會指向上一級的對象
// 3.如果一個函數中有this
// 這個函數中包含多個對象
// 儘管這個函數是被最外層的對象所調用
// this卻會指向它的上一級對象
var obj = {
a:1;
b:{
// a:2,
fn:function(){
console.log(this.a); // undefined
}
}
}
obj.b.fn();
// 我們可以看到,對象b中沒有屬性a,這個this指向
// 的也是對象b,因爲this只會指向它的上一級對象
// 不管這個對象中有沒有this要的東西
// 我們再來看一種情況
var obj = {
a:1,
b:{
a:2,
fn:function(){
console.log(this.a); // undefined
console.log(this); // window
}
}
}
var demo = obj.b.fn;
demo();
// 在上面的代碼中,this指向的是window
// 你們可能會覺得很奇怪
// 其實是這樣的,有一句話很關鍵,再次敲黑板
// this永遠指向的都是最後調用它的對象
// 也就是看它執行的時候是誰調用的
// 上面的例子中雖然函數fn是被對象b所引用了
// 但是在將fn賦值給變量demo的時候並沒有執行
// 所以最終this指向的是window
四、青梅竹馬
function returnThis(){
return this;
}
var user = {name:"前端食堂"};
returnThis(); // window
returnThis.call(user); // 前端食堂
returnThis.apply(user) ; // 前端食堂
// 這裏就是Object.prototype.call
// 和Object.prototype.apply方法
// 他們可以通過參數來指定this
五、矢志不渝
function returnThis(){
return this;
}
var user1 = {name:"前端食堂"};
var user1returnThis = returnThis.bind(user1);
user1returnThis(); // 前端食堂
var user2 = {name:"前端小食堂"};
user1returnThis.call(user2); // still 前端食堂
// Object.prototype.bind通過一個新函數來提供了永久的綁定
// 而且會覆蓋call和apply的指向
六、乾坤大挪移
function Fn(){
this.user = "前端食堂";
}
var demo = new Fn();
console.log(demo.user); // 前端食堂
// 這裏new關鍵字改變了this的指向
// new關鍵字創建了一個對象實例
// 所以可以通過對象demo點語法點出函數Fn裏面的user
// 這個this指向對象demo
// 注意:這裏new會覆蓋bind的綁定
function demo(){
console.log(this);
}
demo(); // window
new demo(); // demo
var user1 = {name:"前端食堂"};
demo.call(user1); // 前端食堂
var user2 = demo.bind(user1);
user2(); // 前端食堂
new user2(); // demo
七、愛轉角遇上return
// 當this遇上return時
function fn(){
this.user = "前端食堂";
return{};
}
var a = new fn;
console.log(a.user); // undefined
function fn(){
this.user = "前端食堂";
return function(){};
}
var a = new fn;
console.log(a.user); // undefined
function fn(){
this.user = "前端食堂";
return 1;
}
var a = new fn;
console.log(a.user); // 前端食堂
function fn(){
this.user = "前端食堂";
return undefined;
}
var a = new fn;
console.log(a.user); // 前端食堂
// 總結:如果返回值是一個對象
// 那麼this指向就是返回的對象
// 如果返回值不是一個對象
// 那麼this還是指向函數的實例
// null比較特殊,雖然它是對象
// 但是這裏this還是指向那個函數的實例
function fn(){
this.user = "前端食堂";
return null;
}
var a = new fn;
console.log(a.user); // 前端食堂
八、英雄登場
// 最後我們介紹一種在ES6中的箭頭函數
// 這個箭頭函數中的this被加里奧的英雄登場錘的不行
// 皮不起來了
// 而且,在代碼運行前就已經被確定了下來
// 誰也不能把它覆蓋
// 這樣是爲了方便讓回調函數中this使用當前的作用域
// 讓this指針更加的清晰
// 所以對於箭頭函數中的this指向
// 我們只要看它創建的位置即可
function callback(qdx){
qdx();
}
callback(()=>{console.log(this)}); // window
var user = {
name:"前端食堂",
callback:callback,
callback1(){
callback(()=>{console.log(this)});
}
}
user.callback(()=>{console.log(this)}); // still window
user.callback1(()=>{console.log(this)}); // user
怎麼樣?this其實不過如此吧。再皮也要治住他~
交流
歡迎來我的個人公衆號交流,優質原創文章將同步推送。後臺回覆福利,即可領取福利,你懂得~
你的前端食堂,記得按時吃飯。