JavaScript核心技術(shù)開發(fā)解密讀書筆記(第七章)

第七章 this

去年校招的時候,關(guān)于this基本每家公司必問。或者是出題考你,或者是讓你簡述改變this原理。關(guān)于this,個人認為本書講的不如《你不知道的JavaScript(上)》講得好,這本書只適合入門,如想深挖,還是建議童鞋們看下上面提到的那本書。
關(guān)于this,無論哪本書,都會提到這樣一點,這也是學好this的前提。

當前函數(shù)的this是在函數(shù)被調(diào)用執(zhí)行的時候才確定的。

記住這一點后,我將結(jié)合《你不知道的JavaScript(上)》,對this進行介紹。

1. 默認綁定

首先要介紹的是最常用的函數(shù)調(diào)用類型:獨立函數(shù)調(diào)用。可以把這條規(guī)則看作是無法應用其他規(guī)則時的默認規(guī)則。

function foo () {
  console.log(this.a);
}
var a = 2;
foo(); // 2

當調(diào)用foo()時,this.a被解析成了全局變量a,函數(shù)調(diào)用時應用了this的默認綁定,因此this指向全局對象。
如果使用嚴格模式,那么全局對象將無法使用默認綁定,因此this會綁定到undefined:

function foo () {
  "use strict"
  console.log(this.a);
}
var a = 2;
foo(); // TypeError: this is undefined
2. 隱式綁定

先看一段代碼。

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

當foo()被調(diào)用時,它的落腳點確實指向obj對象。當函數(shù)引用有上下文對象時,隱式綁定規(guī)則會把函數(shù)調(diào)用中的this綁定到這個上下文對象。因為調(diào)用foo()時this被綁定到obj,因此this.a和obj.a是一樣的。
對象屬性引用鏈中只有最頂層或者說只有最后一層影響調(diào)用位置。

function foo () {
  console.log(this.a);
}
var obj2 = {
  a: 42,
  foo: foo
};
var obj1 = {
  a: 2,
  obj2: obj2
};
obj1.obj2.foo(); // 42

隱式丟失
還是先看一段代碼。

function foo () {
  console.log(this.a);
}
var obj = {
  a: 2,
  foo: foo
};
var bar = obj.foo;
var a = 'oops, global';
bar(); // 'oops, global'

雖然bar是obj.foo的一個引用,但是實際上,它引用的是foo函數(shù)本身,因此此時的bar其實是一個不帶任何修飾的函數(shù)調(diào)用,因此應用了默認綁定。
一種更微妙、更常見并且更出乎意料的情況發(fā)生在傳入回調(diào)函數(shù)時:

function foo () {
  console.log(this.a);
}
function doFoo (fn) {
  fn();
}
var obj = {
  a: 2,
  foo: foo
};
var a = 'oops, global';
doFoo(obj.foo); // 'oops, global'

參數(shù)傳遞其實就是一種隱式賦值,因此我們傳入函數(shù)時也會被隱式賦值。
JavaScript環(huán)境中內(nèi)置的setTimeout()函數(shù)實現(xiàn)和下面的偽代碼類似:

function setTimeout (fn, delay) {
  // 等待delay毫秒
  fn(); // 調(diào)用位置
}

關(guān)于隱形丟失,《JavaScript核心技術(shù)開發(fā)解密》一書給出兩道還不錯的思考題。這兩道題這里不再給出答案,有興趣的同學請思考后,使用控制臺自行驗證。

// 思考題1
function foo () {
  console.log(this.a);
}
function active (fn) {
  fn();
}
var a = 20;
var obj = {
  a:10,
  getA: foo,
  active: active
}
active(obj.getA);
obj.active(obj.getA);

// 思考題2
var n = 'window';
var object = {
  n: 'object',
  getN: function () {
    return function () {
      return this.n;
    }
  }
}
console.log(object.getN()());
3. 顯示綁定

JavaScript可以使用函數(shù)的call和apply方法改變this的指向。它們的第一個參數(shù)是一個對象,它們會把這個對象綁定到this,接著在調(diào)用函數(shù)時指定這個this。因為你可以直接指定this的綁定對象,因此我們稱之為顯示綁定。

function foo () {
  console.log(this.a);
}
var obj = {
  a: 2
};
foo.call(obj); // 2

通過foo.call(),我們可以在調(diào)用foo時強制把它的this綁定到obj上。
可惜顯示綁定仍然無法解決我們之前提出的丟失綁定問題。
硬綁定
先看一段代碼。

function foo () {
  console.log(this.a);
}
var obj = {
  a: 2
};
var bar = function () {
  foo.call(obj);
};
bar(); // 2
setTimeout(bar, 100); // 2
// 硬綁定的bar不可能再修改它的this
bar.call(window); // 2

我們創(chuàng)建了函數(shù)bar(),并在它的內(nèi)部手動調(diào)用了foo.call(obj),因此強制把foo的this綁定到了obj。無論之后如何調(diào)用函數(shù)bar,它總會手動在obj上調(diào)用foo,這種綁定是一種顯示的強制綁定,因此我們稱之為硬綁定。
由于硬綁定是一種非常常用的模式,所以在ES5中提供了內(nèi)置的Function.prototype.bind,它的用法如下:

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
console.log(b); // 5 

call/apply/bind的特性讓JavaScript變得十分靈活,它們的應用場景十分廣泛,例如,將類數(shù)組轉(zhuǎn)化為數(shù)據(jù)、實現(xiàn)繼承、實現(xiàn)函數(shù)柯里化等。

4. new綁定

JavaScript有一個new操作符,使用方法看起來和那些面向類的語言一樣,然而,JavaScript中new的機制實際上和面向類的語言完全不同。
使用new來調(diào)用函數(shù),或者說發(fā)生構(gòu)造函數(shù)調(diào)用時,會自動執(zhí)行下面的操作。
1.創(chuàng)建(或者說構(gòu)造)一個全新的對象。
2.這個新對象會被執(zhí)行[[原型]]連接。
3.這個新對象會綁定到函數(shù)調(diào)用的this。
4.如果函數(shù)沒有返回其他對象,那么new表達式中的函數(shù)調(diào)用會自動返回這個信息對象。

function foo (a) {
  this.a = a;
}
var bar = new foo(2);
console.log(bar.a); // 2

使用new來調(diào)用foo()時,我們會構(gòu)造一個新對象并把它綁定到foo()調(diào)用中的this上。

5. 優(yōu)先級

new綁定>顯式綁定/硬綁定>隱式綁定

以上是我對JavaScript核心技術(shù)開發(fā)解密第七章的讀書筆記,碼字不易,請尊重作者版權(quán),轉(zhuǎn)載注明出處。
By BeLLESS 2018.7.15 21:17

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

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

  • 官方中文版原文鏈接 感謝社區(qū)中各位的大力支持,譯者再次奉上一點點福利:阿里云產(chǎn)品券,享受所有官網(wǎng)優(yōu)惠,并抽取幸運大...
    HetfieldJoe閱讀 7,002評論 15 54
  • 第2章 基本語法 2.1 概述 基本句法和變量 語句 JavaScript程序的執(zhí)行單位為行(line),也就是一...
    悟名先生閱讀 4,542評論 0 13
  • 特別說明,為便于查閱,文章轉(zhuǎn)自https://github.com/getify/You-Dont-Know-JS...
    殺破狼real閱讀 816評論 0 1
  • 一顆瓜子引發(fā)的悲劇。 一個醫(yī)生最痛苦的時候莫過于一個生命就從自己眼前消失;最大的悲哀莫過于對一個生命消失時的那種無...
    叮叮當_6526閱讀 1,141評論 1 1
  • 長這么大,最慶幸的是看到了《英雄本色》這部電影,知道了一個叫做 周潤發(fā)的演員。 說起發(fā)哥,會想到什么呢?風流倜儻...
    李大柘閱讀 2,169評論 15 11

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