《Javascript高級程序設計》讀書筆記之bind函數詳解


為什么需要bind

var name = "The Window";
var object = {
    name: "My Object",
    getNameFunc: function () {
        return function () {
            return this.name;
        }
    }
};

alert(object.getNameFunc()()); //"The Window"

object.getNameFunc()返回一個匿名函數,在全局環境調用該函數,this指向的全局對象

解決這一問題,可以像下面這樣,將匿名函數外部作用域中this對象保存在閉包能夠訪問到的變量中

var name = "The Window";
var object = {
    name: "My Object",
    getNameFunc: function () {
        var that = this;
        return function () {
            return that.name;
        }
    }
};

alert(object.getNameFunc()()); //"My Object"

上述解決方法需要修改對象的方法,如果不能修改原對象的方法,該如何做呢?

這時,我們可以像下面這樣,使用apply或call方法指定函數的作用域

var name = "The Window";
var object = {
    name: "My Object",
    getNameFunc: function () {
        return function () {
            return this.name;
        }
    }
};
var func=object.getNameFunc();
alert(func.apply(object)); //"My Object"

通過apply、call,已經可以輸出預期的My Object

但是,每次調用時都需要以func.apply(object)的形式調用,這不是很怪么

理想的調用方式,當然是在通過某種處理后,之后可以以func()形式調用,像下面這樣

var name = "The Window";
var object = {
    name: "My Object",
    getNameFunc: function () {
        return function () {
            return this.name;
        }
    }
};
var func=object.getNameFunc();
func=func.bind(object);
alert(func()); //"My Object"

ECMAScript 5中的bind

 ECMAScript 5定了了bind方法,這個方法會創建一個函數實例,其this值會被綁定到傳給bind函數的值,上面代碼給出了bind函數的使用方式,再給一個簡單示例。

window.color="red";
var o={color:"blue"};
function sayColor(){
    alert(this.color);
}

var func=sayColor.bind(o);
func();//"blue"

雖然大部分瀏覽器中已經可以使用ECMAScript 5定義的這個方法,但在少數不支持的瀏覽器中你還是會遇到兼容性問題,這是如何處理呢?

通過上面apply、call方法使用示例 ,可以像下面這樣提供一個解決方案

Function.prototype.bind=Function.prototype.bind||
    function(context){
        var self=this;
        return function()
        {
            return self.apply(context,arguments);
        }
    }

Prototype.js中的bind

// The .bind method from Prototype.js 
Function.prototype.bind = function(){ 
  var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift(); 
  return function(){ 
    return fn.apply(object, 
      args.concat(Array.prototype.slice.call(arguments))); 
  }; 
};

 上述代碼中,

args=Array.prototype.slice.call(arguments)將調用bind函數時參數集合arguments轉換為數組array

object=args.shift()將args數組第一個元素取出作為當前對象

匿名函數中,調用args.concat(Array.prototype.slice.call(arguments))是為了將調用匿名函數時傳入的參數與調用bind時參數合並成一個參數數組

以一個調用示例來看上述過程

var obj = { x: 'prop x' };
//args = Array.prototype.slice.call(arguments)后args = [obj, 12, 23 ]
//object=args.shift()后,args =[12, 23] ,object =obj

var boundExample = example.bind(obj, 12, 23);
boundExample(36, 49); // arguments => 36, 49 ,調用args.concat(Array.prototype.slice.call(arguments))后,arguments that our example() function receives => [12, 23, 36, 49]

Firefox中的bind

if (!Function.prototype.bind) {
  Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
      // closest thing possible to the ECMAScript 5 internal IsCallable function
      throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var aArgs = Array.prototype.slice.call(arguments, 1), 
        fToBind = this, 
        fNOP = function () {},
        fBound = function () {
          return fToBind.apply(this instanceof fNOP && oThis
                                 ? this
                                 : oThis || window,
                               aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
  };
}

Firefox為bind提供了一個兼容實現,主要代碼與prototype.js中實現類似,不再逐句解釋了

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM