標籤:lan apply 就是 error script 簡單 def bar rgs
bind方法可以用來給一個方法綁定上下文環境對象,以及重新給方法傳參數。
bind的另一個簡單使用是使一個函數擁有預設的初始參數。我們稱為偏函數
function list() { return Array.prototype.slice.call(arguments);}var list1 = list(1, 2, 3); // [1, 2, 3]// Create a function with a preset leading argumentvar leadingThirtysevenList = list.bind(undefined, 37);var list2 = leadingThirtysevenList(); // [37]var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]
由於bind方法在並不是在所有的瀏覽器上都支援,因此我們考慮自己實現bind方法。
首先我們可以給目標函數指定範圍來簡單實現bind
Function.prototype.bind = function(context){ self = this; return function(){ return self.apply(context, arguments); }}
這樣實現的bind方法就只能接受一個上下文環境變數的參數,不能同時接受參數。因此我們修改一下。
Function.prototype.bind = function(context){ var slice = Array.prototype.slice, _args = slice.call(arguments,1), self = this; return function(){ var _inargs = slice.call(arguments); return self.apply(context, _args.concat(_inargs)); }}
現在bind可以綁定對象,同時也能在綁定對象時傳遞參數。
但是bind還有一個特點:
一個綁定函數也能使用new操作符建立對象:這種行為就像把原函數當成構造器。提供的 this 值被忽略,同時調用時的參數被提供給類比函數。
並看不懂什麼意思 = = 其實就是bind返回的函數還能用做建構函式。bind 時指定的 this 值會失效,但傳入的參數依然生效。
舉個例子:
var value = 2;var foo = { value: 1};function bar(name, age) { this.habit = ‘shopping‘; console.log(this.value); console.log(name); console.log(age);}bar.prototype.friend = ‘kevin‘;var bindFoo = bar.bind(foo, ‘daisy‘);var obj = new bindFoo(‘18‘);// undefined// daisy// 18console.log(obj.habit);console.log(obj.friend);// shopping// kevin
我們可以通過修改函數的返回原型來實現,代碼如下:
Function.prototype.bind = function(context){ var slice = Array.prototype.slice, _args = slice.call(arguments,1), self = this, fBound = function(){ var _inargs = slice.call(arguments); // 當作為建構函式時,this 指向執行個體,此時結果為 true,將綁定函數的 this 指向該執行個體,可以讓執行個體獲得來自綁定函數的值 // 以上面的是 demo 為例,如果改成 `this instanceof fBound ? null : context`,執行個體只是一個Null 物件,將 null 改成 this ,執行個體會具有 habit 屬性 // 當作為普通函數時,this 指向 window,此時結果為 false,將綁定函數的 this 指向 context return self.apply((this instanceof fBound ? this : context), _args.concat(_inargs)); }; // 修改返回函數的 prototype 為綁定函數的 prototype,執行個體就可以繼承綁定函數的原型中的值 fBound.prototype = self.prototype; return fBound;}
bound.prototype = this.prototype
這麼寫的話,修改返回函數原型對象(bound.prototype)的同時把綁定函數的原型對象(this.prototype也同時修改了。因此用匿名函數做中轉,this.protptype 就安全了。
還有幾個小問題的解決:
- 調用bind不是函數
- bind相容性的問題
最終的完整代碼如下:
fakeBind = function (context) { if (typeof this !== "function") { throw new Error("Function.prototype.bind - what is trying to be bound is not callable"); } var slice = Array.prototype.slice, _args = slice.call(arguments, 1), self = this, F = function () {}, bound = function () { var _inargs = slice.call(arguments); return self.apply((this instanceof F ? this : context), _args.concat(_inargs)); }; F.prototype = self.prototype; bound.prototype = new F(); return bound;}Function.prototype.bind = Function.prototype.bind || fakeBind;
ES6版實現
Function.prototype.fakeBindES6 = function(context, ...rest) { if (typeof this !== "function") { throw new Error("Bind must be called on a function"); } var self = this; return function inner(...args) { if (this instanceof inner) { // 當返回的內層函數作為建構函式使用,bind 時綁定的 this 失效。 // 即此處直接執行綁定函數,而不使用 apply 對 this 進行綁定 return new self(...rest, ...args); } // 當作為普通函數調用,this 指向傳入的對象 return self.apply(context, rest.concat(args)); };};
Github: https://github.com/Vxee/articles/issues/7
javascript 原生bind方法實現