📓
Something About JavaScript
  • Introduction
  • Day 1:前言
  • Day 2:資料型態的夢魘——動態型別加弱型別(1)
  • Day 3:資料型態的夢魘——動態型別加弱型別(2)
  • Day 4:動態型別加弱型別不是罪——怎麼 JavaScript 一摔就變成個印度阿三?
  • Day 5:湯姆克魯斯與唐家霸王槍——變數的作用域(Scope) (1)
  • Day 6:湯姆克魯斯與唐家霸王槍——變數的作用域(Scope) (2)
  • Day 7:傳統 var 關鍵字的不足
  • Day 8:var 掰掰 —— ES6 更嚴謹安全的 let 和 const
  • Day 9:圖解變數作用域(Scope)
  • Day 10:程式也懂電梯向上? —— Hoisting
  • Day 11:Strict Mode (嚴謹模式)
  • Day 12:看 Strict Mode 如何施展「還我漂亮拳」(1)
  • Day 13:看 Strict Mode 如何施展「還我漂亮拳」(2)
  • Day 14:來挖挖恐龍骨 —— with 語法
  • Day 15:this 關鍵字 (1)
  • Day 16:this 關鍵字 (2)
  • Day 17:this 關鍵字 (3)
  • Day 18:this 關鍵字 (4)
  • Day 19:函數定義 (Function Definition) 的 100 種寫法
  • Day 20:ES6 的箭頭函數 (Arrow Functions)
  • Day 21:箭頭函數 (Arrow Functions) 的 this 和你想的不一樣 (1)
  • Day 22:箭頭函數 (Arrow Functions) 的 this 和你想的不一樣 (2)
  • Day 23:ES6 物件實字威力加強版 (Enhanced Object Literals)
  • Day 24:函數呼叫 (Function Invocation) 與立即函數 (Self-Invoking Functions)
  • Day 25:不是多了塊魚 —— 立即函數的應用整理
  • Day 26:程式界的哈姆雷特 —— Pass by value, or Pass by reference?
  • Day 27:別管變數 Pass by Whatever,尋找容易理解的銀色子彈 (Silver Bullet)
  • Day 28:閉包 (Closures)
  • Day 29:閉包 (Closures) 進階打怪實戰
  • Day 30:ES10 醞釀中 —— 擁抱 JS の 未來
Powered by GitBook
On this page
  • 函數執行環境下 (Function Context) (續)
  • 5. 顯性函數綁定之 call()/apply() 篇 (Explicit Function Binding for call()/apply())
  • 6. this 指向 new 所產生的新物件
  • References

Was this helpful?

Day 17:this 關鍵字 (3)

函數執行環境下 (Function Context) (續)

5. 顯性函數綁定之 call()/apply() 篇 (Explicit Function Binding for call()/apply())

this 物件:函數物件被執行時所指定的綁定物件,也就是 Function.prototype.call() 或 Function.prototype.apply() 的第一個參數。

由於 apply() 和 call() 幾乎一樣,差別只在於參數的形式不同:

  • call() 接受一連串獨立的參數。

  • apply() 接受一組陣列形式的參數。

console.log( Math.max.call(null, 40, 100, 1, 5, 25, 10) );      //100
console.log( Math.max.apply(null, [40, 100, 1, 5, 25, 10]) );   //100

因此以下範例只用 apply() 來示範。

5.1. 和 bind() 的差別

apply() / call() 和上一節介紹的 bind() 非常相似,都是對函數物件做綁定物件的指定,使用的語法形式也很像。

但同樣對某個函數物件 A 使用時, bind() 和 apply() / call() 的差別在於:

  • bind() 會建立一個新的函數物件 B,為 B 綁定一個特定物件,然後回傳 B 物件本身。B 會繼承了 A 的原型。

    • 如果 A 已經有用 bind() 綁定過,B 也會繼承相同的綁定物件,無法再綁新的 (前面 4.3 的範例)。

  • apply() / call() 會執行函數物件 A,指派一個物件作為 this,然後回傳函數 A 的執行結果。

    • 如果 A 已經有用 bind() 綁定過,用 apply() / call() 再指派其他的物件也沒用。

5.2. 綁定物件範例 (一般模式 & 嚴謹模式)

由於一般模式和嚴謹模式的行為一樣,這裡以一般模式示範。

var whatsThis = function() {
    return this;
};
var getFullName = function() {
    return this.firstName + " " + this.lastName;
}

var ironMan = { firstName: "Tony", lastName : "Stark" };
var captainAmerica = { firstName: "Steven", lastName : "Rogers" };

console.log(whatsThis.apply(ironMan));          // {firstName: "Tony", lastName: "Stark"}
console.log(getFullName.apply(ironMan));        // "Tony Stark"
console.log(whatsThis.apply(captainAmerica));   // {firstName: "Steven", lastName: "Rogers"}
console.log(getFullName.apply(captainAmerica)); // "Steven Rogers"

直接以要執行的函數物件本身 (e.g., whatsThis, getFullName) 去呼叫 apply(),第一個參數就是函數裡的 this。

透過這種方式指定函數的綁定物件,也不需要另外建立一個新的函數物件,即插即用。在使用靈活性上,我認為比 bind() 方便。

5.3. 對已經被 Binding 過的函數物件無效

上一節提過,bind() 就像海誓山盟,一個函數物件只要曾經和某個物件進行綁定就會死心踏地,即使再進行二次 bind() 或利用 apply() 指派一次性的綁定物件,都不會生效。

var getFullName = function() {
    return this.firstName + " " + this.lastName;
}

var ironMan = { firstName: "Tony", lastName : "Stark" };
var spiderMan = { firstName: "Peter", lastName : "Parker"};

var introIronMan = getFullName.bind( ironMan );
var introClone = introIronMan.bind();
var introSpiderMan = introIronMan.bind( spiderMan );

console.log(introIronMan());                    // "Tony Stark"
console.log(introSpiderMan());                  // "Tony Stark"
console.log(introClone());                      // "Tony Stark"
console.log(introClone.apply(spiderMan));       // "Tony Stark"
console.log(introSpiderMan.apply(spiderMan));   // "Tony Stark"
console.log(getFullName.apply(spiderMan));      // "Peter Parker"

以下對每個函數物件的綁定狀況逐一解說:

  • getFullName() 這個函數物件本身沒有綁定過任何物件。

  • introIronMan() 是透過 getFullName() 產生的新函數物件,綁定了 ironMan。

  • introClone() 是透過 introIronMan() 產生的新函數物件,雖然語法沒有指定新的綁定物件,但透過繼承,同樣綁定了 ironMan。

  • introSpiderMan() 是透過 introIronMan() 產生的新函數物件,雖然語法指定了新的綁定物件 spiderMan,但由於繼承了 introIronMan() 的綁定關係,因此綁定物件同樣是 ironMan。

  • introClone() 和 introSpiderMan() 因為本身函數物件已經存在綁定了 ironMan 的關係,即使透過 apply() 指派新的物件,執行時函數內的 this 永遠仍是 ironMan。

  • getFullName() 因為沒有存在任何綁定關係,因此透過 apply(),可以成功將函數內的 this 指派為 spiderMan。

6. this 指向 new 所產生的新物件

this 物件:new 所產生的新物件。

當對函數使用 new 關鍵字來產生一個物件,該物件會形成自己的環境 (Context),例如以下範例:

function Hero(n){
    this.exp = n;
};

var h = new Hero(100);
console.log(h);         // Hero {exp: 100}
console.log(h.exp);     // 100
  • 原本函數內的 this.exp 變成新物件 h 的屬性。

  • 一般模式和嚴謹模式是一樣的行為。

References

PreviousDay 16:this 關鍵字 (2)NextDay 18:this 關鍵字 (4)

Last updated 4 years ago

Was this helpful?

W3Schools - The JavaScript this Keyword
this - JavaScript | MDN
#Javascript:this用法整理 | 英特尔® 软件
JavaScript 語言核心(11)this 是什麼? by caterpillar | CodeData