this的指向問題

問題由來

  var obj = {
    foo: function () { 
      console.log(this.bar) 
    },
    bar: 1
  };
  var foo = obj.foo;
  var bar = 2;
  obj.foo() // 1
  foo() // 2  

雖然obj.foofoo指向同一個(gè)函數(shù),但是執(zhí)行結(jié)果可能不一樣。
this指的是函數(shù)運(yùn)行時(shí)所在的環(huán)境,對于obj.foo來說,foo運(yùn)行在obj環(huán)境,所以this指向obj;對于foo()來說,foo()運(yùn)行在全局環(huán)境,所以this指向全局環(huán)境。

函數(shù)的運(yùn)行環(huán)境到底是怎么決定的?

內(nèi)存的數(shù)據(jù)結(jié)構(gòu)

  1. 將對象賦值給變量obj
    var obj = { foo: 5 };
    JavaScript 引擎會(huì)先在內(nèi)存里面,生成一個(gè)對象{ foo: 5 },然后把這個(gè)對象的內(nèi)存地址賦值給變量obj
    即變量obj是一個(gè)地址。后面如果要讀取obj.foo,引擎先從obj拿到內(nèi)存地址,然后再從該地址讀出原始的對象,返回它的foo屬性。

    avatar

  2. 將函數(shù)賦值給變量obj
    var obj = { foo: function () {} };
    JavaScript 引擎會(huì)將函數(shù)單獨(dú)保存在內(nèi)存中,然后再將函數(shù)的地址賦值給foo屬性的value屬性。

    avatar

由于函數(shù)是一個(gè)單獨(dú)的值,所以它可以在不同的環(huán)境(上下文)執(zhí)行。

this指向的規(guī)律

  • 在函數(shù)體中,簡單調(diào)用函數(shù)時(shí)(非顯示/隱式綁定下),嚴(yán)格模式下 this 綁定到undefined,否則綁定到全局對象 window/global;
  • 一般由上下文對象調(diào)用,綁定在該對象上;
  • 一般由 bind/call/apply 方法顯示調(diào)用,綁定到指定參數(shù)的對象上;
  • 一般構(gòu)造函數(shù)new調(diào)用,綁定到新創(chuàng)建的對象上;
  • 箭頭函數(shù)中,根據(jù)外層上下文綁定的this決定this的指向。

四類場景

  1. "test()"形式
  function f1(){
   console.log(this)  
 }
 var  arr = [f1,2,3];   
 var f2 = arr[0];
 f2();        

<details>
<summary>答案</summary>
<pre>
this指向 window/嚴(yán)格模式下undefined
</pre>
</details>
直接不帶任何引用形式去調(diào)用函數(shù),則this會(huì)指向全局對象,因?yàn)闆]有其他影響去改變this,this默認(rèn)就是指向全局對象(瀏覽器是window,Node中是global)的。這個(gè)結(jié)論是在非嚴(yán)格模式的情況下,嚴(yán)格模式下這個(gè)this其實(shí)是undefined的。

  1. "XXX.test()"形式
  var a = 1
  function test(){
    console.log(this.a)
  }
  var obj = {
    a:2,
    test
  }
  obj.test() 

<details>
<summary>答案</summary>
<pre>
2
</pre>
</details>
一句話,誰去調(diào)用這個(gè)函數(shù)的,這個(gè)函數(shù)中的this就綁定到誰身上。

測試題

  var a = 1
  function test(){
    console.log(this.a)
  }
  var obj = {
    a:2,
    test
  }
  var testCopy = obj.test
  testCopy()

<details>
<summary>答案</summary>
<pre><code>
1
</code></pre>
</details>

  var a = 1
  function test(){
    console.log(this.a)
  }
  var obj = {
    a:2,
    test
  }
  var obj0 = {
      a:3,
      obj
  }
  obj0.obj.test()

<details>
<summary>答案</summary>
<pre>
<code>
2
即使是這種串串燒的形式,結(jié)果也是一樣的,test()中的this只對直屬上司(直接調(diào)用者obj)負(fù)責(zé)。
</code>
</pre>
</details>

  1. "test.call(xxx) / test.apply(xxx) / test.bind()"形式
  var a = 1
  function test(){
    console.log(this.a)
  }
  var obj = {
    a:2,
    test
  }
  var testCopy = obj.test
  testCopy.call(obj)

<details>
<summary>答案</summary>
<pre><code>
2
</code></pre>
</details>
可以看到,我們通過call/apply來調(diào)用testCopy,并且傳入了你想要 this 指向的上下文,那么this就會(huì)按照你的指示行事。

  1. "new test()"形式
  var a = 1
  function test(a){
    this.a = a
  }
  var b = new test(2)
  console.log(b.a)

<details>
<summary>答案</summary>
<pre><code>
2
</code></pre>
</details>
new這個(gè)操作符其實(shí)是new了一個(gè)新對象出來,而被new的test我們稱為構(gòu)造函數(shù),我們可以在這個(gè)構(gòu)造函數(shù)里定義一下將要到來的新對象的一些屬性。那么在構(gòu)造函數(shù)里,我們怎樣去描述這個(gè)還未出生的新對象呢?沒錯(cuò),就是用this。所以構(gòu)造函數(shù)里的this指的就是將要被new出來的新對象。

特別的 箭頭函數(shù)

  var a = 1;
  var test = () => {
    var a = 3;
    console.log(this.a)
  }
  var obj = {
    a: 2,
    test
  }
  obj.test()

<details>
<summary>答案</summary>
<pre><code>
1
</code></pre>
</details>
箭頭函數(shù)中this對象就是定義時(shí)所在的作用域,也就是說箭頭函數(shù)本身沒有this,內(nèi)部的this就是外層代碼塊作用域中的this。

測試題

  var a = 1
  var obj = {
    a: 2,
    test: ()=> {
      console.log(this.a)
    }
  }
  obj.test()  

<details>
<summary>答案</summary>
<pre><code>
1
func在foo調(diào)用時(shí)定義,此時(shí)的foo所在作用域?yàn)閛bj,因此this指向obj
</code></pre>
</details>

  var a = 1
  function foo(){
    var test = () => {
      console.log(this.a)
    }
    return test
  }
  var obj = {
    a : 2,
    foo:foo
  }
  obj.foo()()  
  

<details>
<summary>答案</summary>
<pre><code>
2
</code></pre>
</details>

隨機(jī)測試題

  var b = {
    a: 23,
    c: 3,
    d: {
      a: 78,
      e: {
        a: 100,
        f: function () {
          console.log(this.a);
        }
      }
    }
  }
  var fn = b.d.e.f;
  fn();
  b.d.e.f(); 

<details>
<summary>答案</summary>
<pre><code>
undefined
100
</code></pre>
</details>

  var point = { 
    x : 0, 
    y : 0, 
    moveTo : function(x, y) { 
      this.x = x;
      console.log(this.x); 
      console.log(this);   

      var moveX = function(x) { 
        this.x = x;
      }; 
      var moveY = function(y) { 
        this.y = y;
      } 
      moveX(x); 
      moveY(y); 
    } 
  }; 
  point.moveTo(1, 1); 
  console.log(point.x); 
  console.log(point.y);
  console.log(x); 
  console.log(y);

<details>
<summary>答案</summary>
<pre><code>
var point = {
x : 0,
y : 0,
moveTo : function(x, y) {
this.x = x;
console.log(this.x); // 1
console.log(this); // point對象
var moveX = function(x) {
this.x = x;
};
var moveY = function(y) {
this.y = y;
}
moveX(x);
moveY(y);
}
};
point.moveTo(1, 1);
console.log(point.x); // 1
console.log(point.y); // 0
console.log(x); // 1
console.log(y);// 1
</code>

point對象下的moveTo方法中的moveX與moveX方法在調(diào)用時(shí)都是全局調(diào)用,綁定的對象都是window。
</pre>
</details>

  var point = { 
    x : 0, 
    y : 0, 
    moveTo : function(x, y) { 
        var that = this; 
        var moveX = function(x) { 
            that.x = x; 
        }; 
        var moveY = function(y) { 
            that.y = y;
        } 
        moveX(x); 
        moveY(y); 
    } 
  }; 
  point.moveTo(1, 1); 
  console.log(point.x); 
  console.log(point.y); 
  console.log(x) 
  console.log(y) 

<details>
<summary>答案</summary>
<pre><code>
var point = {
x : 0,
y : 0,
moveTo : function(x, y) {
var that = this; //內(nèi)部變量替換
// 內(nèi)部函數(shù)
var moveX = function(x) {
that.x = x;
};
// 內(nèi)部函數(shù)
var moveY = function(y) {
that.y = y;
}
moveX(x); //這里依然是全局調(diào)用,但是在給變量賦值時(shí),不再是this指向,而是that指向,而that指向的對象是 point。
moveY(y);
}
};
point.moveTo(1, 1);
console.log(point.x); // 1
console.log(point.y); // 1
console.log(x) // 報(bào)錯(cuò) x is not defined
console.log(y) //
</code></pre>
</details>

const obj = {
  name: " jsCoder",
  skill: ["es6", "react", "angular"],
  say: function() {
    for (var i = 0, len = this.skill.length; i < len; i++) {
      setTimeout(function() {
        console.log("No." + i + this.name);
        console.log(this.skill[i]);
        console.log("--------------");
      }, 0);
      console.log(i);
    }
  }
};
obj.say();

參考地址

不要再問我this的指向問題了_個(gè)人文章 - SegmentFault 思否
JavaScript 的 this 原理 - 阮一峰的網(wǎng)絡(luò)日志

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. this指向問題 1.1 認(rèn)識詞法作用域 其實(shí)我們js中的作用域就是詞法作用域,我們會(huì)發(fā)現(xiàn)詞法作用域最重要的...
    時(shí)光如劍閱讀 1,036評論 0 8
  • 最近有點(diǎn)閑暇時(shí)間,就來總結(jié)js中this的指向問題,如有不對,請指出。this指向,網(wǎng)上做多的描述是指向那個(gè)最終調(diào)...
    AlisaMfz閱讀 601評論 0 0
  • 要徹底理解JS中的this指向問題,建議多結(jié)合一些相關(guān)的面試題,理解記憶,不必硬背 關(guān)于this問題:只需記住誰調(diào)...
    追馬的時(shí)間種草閱讀 175評論 0 0
  • 一:this 是在函數(shù)被調(diào)用時(shí)確定的,它的指向完全取決于函數(shù)調(diào)用的地方,而不是它被聲明的地方(除箭頭函數(shù)外)。當(dāng)一...
    張艷華_zzz閱讀 265評論 0 0
  • 今天感恩節(jié)哎,感謝一直在我身邊的親朋好友。感恩相遇!感恩不離不棄。 中午開了第一次的黨會(huì),身份的轉(zhuǎn)變要...
    余生動(dòng)聽閱讀 10,810評論 0 11

友情鏈接更多精彩內(nèi)容