如何治療JavaScript中的this?

111.jpg

觀感度:🌟🌟🌟🌟🌟javascript

口味:清爽綠豆前端

烹飪時間:15minjava

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

11.gif

衆所周知,this在JavaScript中的指向一直很難讓人理解,想要學好JavaScript,this也是咱們必需要搞清楚的。其實,this並無那麼難,本文將力爭帶你治療this的「皮」。瀏覽器

首先,來科普三個問題!app

this是什麼?

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

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

「皮卡!皮卡!皮卡丘!」spa

「斯卡!斯卡!斯卡納!」 prototype

12.jpg

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

爲何要使用this?

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

通俗地說:就是說咱們能夠把API設計的更加簡潔並且易於複用。

說人話:那就是this能夠幫咱們省略參數。

this的指向

只須要理解並記住一句話,外加幾種小狀況,你們就能夠完徹底全的理解this

好,注意聽講。

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

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

好,那縮短點。

一句話。

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

總結:

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

2.this能夠幫咱們省略參數。

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

是否是很簡單,搞清楚了這三點,咱們下面將從八種狀況來完全理解this的指向問題。

直接上代碼。

1、直到世界盡頭

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();

// 能夠發現和上面的代碼結果同樣

2、畫龍點睛

var obj = {
  user:」前端食堂」,
  fn:function(){
     console.log(this.user);   // 前端食堂
  }
}

obj.fn();

// 這裏的this指向對象obj
// 注意看最後一行調用函數的代碼
// obj.fn();

// 重要的事情說兩遍!!
// this的指向在函數建立時是決定不了的
// 在調用的時候才能夠決定,誰調用就指向誰

// this的指向在函數建立時是決定不了的
// 在調用的時候才能夠決定,誰調用就指向誰

3、點石成金

// 其實以上兩點說的還不夠準確,咱們接着往下看

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

4、兩小無猜

function returnThis(){
  return this;
}
var user = {name:"前端食堂"};

returnThis();            // window
returnThis.call(user);   // 前端食堂
returnThis.apply(user) ; // 前端食堂

// 這裏就是Object.prototype.call
// 和Object.prototype.apply方法
// 他們能夠經過參數來指定this

5、矢志不渝

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的指向

6、乾坤大挪移

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

7、愛轉角趕上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); // 前端食堂

8、英雄登場

在這裏插入圖片描述

// 最後咱們介紹一種在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

相關文章
相關標籤/搜索