如何治療JavaScript中的this?

觀感度:🌟🌟🌟🌟🌟

口味:清爽綠豆

烹飪時間:15min

你皮任你皮,我當你瓜皮。

11.gif

衆所周知,this在JavaScript中的指向一直很難讓人理解,想要學好JavaScript,this也是我們必須要搞清楚的。其實,this並沒有那麼難,本文將力爭帶你治療this的“皮”。

首先,來科普三個問題!

this是什麼?

this是聲明函數時附加的參數,指向特定的對象,也就是隱藏參數。

比如LOL中各種隱藏的彩蛋,當水晶先鋒斯卡納在草叢裏停留超過15秒不動,會模仿寵物小精靈。

“皮卡!皮卡!皮卡丘!”

“斯卡!斯卡!斯卡納!”

12.jpg

好,相信大家已經理解什麼是this了,就是個隱藏參數,沒有多麼的神奇,其實 每個函數都可以訪問this

爲什麼要使用this?

this提供了一種更加優雅的方式來隱式的傳遞對象引用。

通俗地說:就是說我們可以把API設計的更加簡潔而且易於複用。

說人話:那就是this可以幫我們省略參數。

this的指向

只需要理解並記住一句話,外加幾種小情況,大家就可以完完全全的理解this

好,注意聽講。

這句話就是“this的指向在函數聲明的時候是不會被確定的,只有函數執行的時候才被確定,this最終指向的是調用它的對象”。

有人說這也太長了,記不住。

好,那縮短點。

一句話。

this的指向決定於函數的調用方式”。

總結:

1.this是聲明函數時附加的參數,指向特定的對象,也就是隱藏參數。

2.this可以幫我們省略參數。

3.this的指向決定於函數的調用方式。

是不是很簡單,搞清楚了這三點,我們下面將從八種情況來徹底理解this的指向問題。

直接上代碼。

一、直到世界盡頭

13.jpg

對,沒錯,有沒有想起灌籃高手主題曲

我們首先要了解一下世界觀,分爲三種情況。

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其實不過如此吧。再皮也要治住他~

交流

歡迎來我的個人公衆號交流,優質原創文章將同步推送。後臺回覆福利,即可領取福利,你懂得~

你的前端食堂,記得按時吃飯。

gongzhonghao.png

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章