Javascript 中 this 關鍵詞

圖片發自簡書App

我相信很多前端開發人員都跟我一樣,提起this, 都能說出很多愛恨情仇...

誤區

首先我們說說this的很多錯誤理解版本:

函數本身

這是初學者最容易走進的誤區,如果我說this不是指函數本身,他們會覺得自己那么多年英文白學了,學習this,首先要走出這個誤區。
注意: 很多時候我們確實需要在一個方法中獲得這個方法本身的引用, 比如遞歸調用。有以下兩種情況可供參考:

  1. 實名函數
public static int sum(int num){ 
    if(1 == num){ 
      return 1; 
    } else { 
      return num + sum(num - 1); 
    } 
  }; 

sum()可以直接通過它的名字sum在方法中獲得方法本身的引用。

  1. 匿名函數
setTimeout(function(){
   if(some condition)
      setTimeout(arguments.callee, 0); //arguments.callee is deprecated and may be removed.
}, 0);
函數的主人

this既然不是函數自己,那是什么呢?會不會是這個方法的所屬對象?答案顯然是no。

this究竟指什么?

在解釋這個問題之前,我們先了解一個概念:

call-site

In programming, a call site of a function or subroutine is the location (line of code) where the function is called (or may be called, through dynamic dispatch). A call site is where zero or more arguments are passed to the function, and zero or more return values are received. (from wikipedia)

根據維基百科定義,call-site指函數被調用的位置,并且在這個位置,函數獲得了它的context。看懂了吧,this仍然可以從字面上理解為這里,但不是方法里,而是方法被調用的位置這里,因為它是在方法調用時被定義的,而不是方法聲明時被定義的。

博主:說到這里大家應該知道this指什么了吧,那本文可以結束了...
讀者:什么,這就完了,太抽象了,可以講點具體的嗎???
哈哈,想知道this指什么,只需要分清楚以下幾種情況:

獨立函數調用

this指全局的Window對象或者undefined

var simpleFunction= function () {
  console.log(this);//嚴格模式下 undefined, 非嚴格模式下[object Window]
};
simpleFunction();//call-site
特定上下文調用

this指方法所屬的對象

var myObject = {}
myObject .simpleFunction= function () {
  console.log(this);// myObject
};
myObject .simpleFunction();

請區別以下代碼:

var myObject = {}
myObject .simpleFunction= function () {
  console.log(this);// myObject
};
var simpleFunction2 = myObject .simpleFunction;
//獲得了simpleFunction的引用,此時simpleFunction2與myObject沒有任何關系
simpleFunction2();//[object Window]
綁定上下文調用

通過apply() call() bind()傳入期望的this

var myObject = {}
myObject .simpleFunction= function () {
  console.log(this);//[object Window]
};
myObject .simpleFunction.call(this);

很多時候,以上3種方法可以幫我解決this混亂的情況。在下面的代碼中,介紹一種叫硬綁定模式:

function foo(something) {
    console.log( this.a, something );
    return this.a + something;
}
var obj = {
    a: 2
};
var bar = foo.bind( obj );
var b = bar( 3 ); // 2 3
new 綁定

設想以下代碼即將運行:

new simpleFunction(arguments)

緊接著,會發生以下事情:

  1. 一個繼承自simpleFunction.prototype的全新對象被創建
  2. simpleFunction被傳入給定參數,然后調用,this被綁定到新創建的對象上(new是一種方法的調用形式,而不是類聲明)。
  3. 方法的返回值變成new表達式的值,如果沒有返回值,就用第1步創建的對象代替
    由上可以看出,new只是一種調用方式,這種調用方式可以指定方法運行時的this
    引用MDN提供的例子:
function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}
var mycar = new Car('Eagle', 'Talon TSi', 1993);

以上代碼創建了一個mycar對象,對象有make model year3個屬性,值分別為'Eagle' 'Talon TSi' 1993

其他情況
setTimeout

setTimeout是自行調用,而且是在全局環境下調用,因此往往會丟失掉期望的this

var obj = {};
obj.myMethod = function () {
  console.log(this); // obj
    setTimeout(function () {
        console.log(this); // [object Window]
    }, 100);
};

可以使用bind

setTimeout(function () {
        console.log(this); 
    }.bind(this, 100));

也可以跳出域的限制

var that = this;
  console.log(this); // this = obj
    setTimeout(function () {
        console.log(that); // that (this) = obj
    }, 100);

某些特殊情況下this已被綁定到好特定上下文。

forEach
function foo(el) {
    console.log( el, this.id );
}
var obj = {
    id: "awesome"
};
[1, 2, 3].forEach( foo, obj ); // 1 awesome  2 awesome  3 awesome

Javascript內建函數幫我們實現了foo.call(obj, index)

addEventListener
var element = document.querySelector('.elem');
var someMethod = function () {
  console.log(this)//<div class="elem"></div>;
};
element.addEventListener('click', someMethod, false);

什么情況下我們會使用this?

未完待續
參考:

  1. https://toddmotto.com/understanding-the-this-keyword-in-javascript/
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new

  2. https://github.com/JoeHetfield/You-Dont-Know-JS/blob/b430e8d5a0ef612eb9c3b90c54891c5be6923685/this%20%26%20object%20prototypes/ch2.md

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容