關於Function.prototype.bind

bind()方法會創建一個新函數,稱爲綁定函數。當調用這個綁定函數時,綁定函數會以創建它時傳入bind()方法的第一個參數作爲 this,傳入 bind() 方法的第二個以及以後的參數加上綁定函數運行時本身的參數按照順序作爲原函數的參數來調用原函數。

實際使用中我們經常會碰到這樣的問題:

js 代碼:
  1. function Person(name){
  2. this.nickname = name;
  3. this.distractedGreeting = function() {
  4.  
  5. setTimeout(function(){
  6. console.log("Hello, my name is " + this.nickname);
  7. }, 500);
  8. }
  9. }
  10.  
  11. var alice = new Person('Alice');
  12. alice.distractedGreeting();
  13. //Hello, my name is undefined

這個時候輸出的this.nickname是undefined,原因是this指向是在運行函數時確定的,而不是定義函數時候確定的,再因爲setTimeout在全局環境下執行,所以this指向setTimeout的上下文:window

以前解決這個問題的辦法通常是緩存this,例如:

js 代碼:
  1. function Person(name){
  2. this.nickname = name;
  3. this.distractedGreeting = function() {
  4. var self = this; // <-- 注意這一行!
  5. setTimeout(function(){
  6. console.log("Hello, my name is " + self.nickname); // <-- 還有這一行!
  7. }, 500);
  8. }
  9. }
  10.  
  11. var alice = new Person('Alice');
  12. alice.distractedGreeting();
  13. // after 500ms logs "Hello, my name is Alice"

這樣就解決了這個問題,非常方便,因爲它使得setTimeout函數中可以訪問Person的上下文。但是看起來稍微一種蛋蛋的憂傷。

但是現在有一個更好的辦法!您可以使用bind。上面的例子中被更新爲:

js 代碼:
  1. function Person(name){
  2. this.nickname = name;
  3. this.distractedGreeting = function() {
  4. setTimeout(function(){
  5. console.log("Hello, my name is " + this.nickname);
  6. }.bind(this), 500); // <-- this line!
  7. }
  8. }
  9.  
  10. var alice = new Person('Alice');
  11. alice.distractedGreeting();
  12. // after 500ms logs "Hello, my name is Alice"

bind() 最簡單的用法是創建一個函數,使這個函數不論怎麼調用都有同樣的 this 值。JavaScript新手經常犯的一個錯誤是將一個方法從對象中拿出來,然後再調用,希望方法中的 this 是原來的對象。(比如在回調中傳入這個方法。)如果不做特殊處理的話,一般會丟失原來的對象。從原來的函數和原來的對象創建一個綁定函數,則能很漂亮地解決這個問題:

js 代碼:
  1. this.x = 9;
  2. var module = {
  3. x: 81,
  4. getX: function() { return this.x; }
  5. };
  6.  
  7. module.getX(); // 81
  8.  
  9. var getX = module.getX;
  10. getX(); // 9, 因爲在這個例子中,"this"指向全局對象
  11.  
  12. // 創建一個'this'綁定到module的函數
  13. var boundGetX = getX.bind(module);
  14. boundGetX(); // 81

瀏覽器支持:

BrowserVersion support
Chrome7
Firefox (Gecko)4.0 (2)
Internet Explorer9
Opera11.60
Safari5.1.4

很不幸,Function.prototype.bind 在IE8及以下的版本中不被支持,所以如果你沒有一個備用方案的話,可能在運行時會出現問題。bind 函數在 ECMA-262 第五版才被加入;它可能無法在所有瀏覽器上運行。你可以部份地在腳本開頭加入以下代碼,就能使它運作,讓不支持的瀏覽器也能使用 bind() 功能。

幸運的是,MDN爲沒有自身實現 .bind() 方法的瀏覽器提供了一個絕對可靠的替代方案:

js 代碼:
  1. if (!Function.prototype.bind) {
  2. Function.prototype.bind = function (oThis) {
  3. if (typeof this !== "function") {
  4. // closest thing possible to the ECMAScript 5 internal IsCallable function
  5. throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
  6. }
  7.  
  8. var aArgs = Array.prototype.slice.call(arguments, 1),
  9. fToBind = this,
  10. fNOP = function () {},
  11. fBound = function () {
  12. return fToBind.apply(this instanceof fNOP && oThis
  13. ? this
  14. : oThis || window,
  15. aArgs.concat(Array.prototype.slice.call(arguments)));
  16. };
  17.  
  18. fNOP.prototype = this.prototype;
  19. fBound.prototype = new fNOP();
  20.  
  21. return fBound;
  22. };
  23. }

參考閱讀:

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

http://www.smashingmagazine.com/2014/01/23/understanding-javascript-function-prototype-bind/

http://krasimirtsonev.com/blog/article/JavaScript-bind-function-setting-a-scope

語法

fun.bind(thisArg[, arg1[, arg2[, …]]])

參數

thisArg
當綁定函數被調用時,該參數會作爲原函數運行時的 this 指向.當使用new 操作符調用綁定函數時,該參數無效.
arg1, arg2, …
當綁定函數被調用時,這些參數加上綁定函數本身的參數會按照順序作爲原函數運行時的參數.

描述

bind() 函數會創建一個新的函數(一個綁定的函數)有同樣的函數體(在 ECMAScript 5 規範內置 Call 屬性),當該函數(綁定函數的原函數)被調用時 this 值綁定到 bind() 的第一個參數,該參數不能被重寫。綁定函數被調用時,bind() 也接受預設的參數提供給原函數。一個綁定函數也能使用 new 操作符 創建對象:這種行爲就像把原函數當成構造器。提供的 this 值被忽略,同時調用時的參數被提供給模擬函數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章