摘要:新函數也能使用操作符創建對象這種行為就像把原函數當成構造器,提供的值被忽略。說明綁定后的新函數被實例化之后,需要繼承原函數的原型鏈方法,且綁定過程中提供的被忽略繼承原函數的對象,但是參數還是會使用。
在討論bind方法前,我們可以先看一個例子:
var getElementsByTagName = document.getElementsByTagName; getElementsByTagName("body");
這樣在瀏覽器(這里使用的是chrome)執行會報錯:
原因也顯而易見:上面的getElementsByTagName方法是document.getElementsByTagName的引用,但是在執行時this指向了global或window對象,而不是document對象。
解決辦法也很簡單,使用call或bind方法來改變this:
var getElementsByTagName = document.getElementsByTagName; getElementsByTagName.call(document, "body");
或
var getElementsByTagName = document.getElementsByTagName; getElementsByTagName.bind(document)("body");
上述兩種解決辦法也可以看出call和bind的區別:call方法是直接執行,而bind方法是返回一個新函數。
實現由于bind方法是從ES5才開始引入的,不是所有瀏覽器都支持,為了實現兼容,需要自己實現bind方法。
我們先來看看bind方法的定義:
bind方法會創建一個新函數。當這個新函數被調用時,bind的第一個參數將作為它運行時的this(該參數不能被重寫), 之后的一序列參數將會在傳遞的實參前傳入作為它的參數。初步思路
新函數也能使用new操作符創建對象:這種行為就像把原函數當成構造器,提供的this值被忽略。
因為bind方法不是立即執行函數,需要返回一個待執行的函數,這里可以利用閉包:return function(){};
作用域綁定:可以使用apply或call方法來實現;
參數傳遞:由于參數的不確定性,需要用apply傳遞數組;
根據上述思路,我們先來實現一個簡單的customBind方法;
Function.prototype.customBind = function (context) { var self = this, /** * 由于參數的不確定性,我們用 arguments 來處理 * 這里的 arguments 只是一個類數組對象,可以用數組的 slice 方法轉化成標準格式數組 * 除了作用域對象 self 以外,后面的所有參數都需要作為數組進行參數傳遞 */ args = Array.prototype.slice.call(arguments, 1); // 返回新函數 return function() { // 作用域綁定 return self.apply(context, args); } };測試初版
var testFn = function(obj, arg) { console.log("作用域對象屬性值:" + this.value); console.log("綁定函數時參數對象屬性值:" + obj.value); console.log("調用新函數參數值:" + arg); } var testObj = { value: 1 }; var newFn = testFn.customBind(testObj, {value: 2}); newFn("hello world"); // 執行結果: // 作用域對象屬性值:1 // 綁定函數時參數對象屬性值:2 // 調用新函數參數值:undefined
從測試執行結果可以看出,上面已經實現了作用域綁定,但是返回新函數newFn不支持傳參,只能在testFn綁定時傳參。
因為我們最終需要使用的是newFn,所以我們需要讓newFn支持傳參。
我們來繼續改造
Function.prototype.customBind = function (context) { var fn = this, args = Array.prototype.slice.call(arguments, 1); return function() { // 將新函數執行時的參數 arguments 全部數組化,然后與綁定時傳參 arg 合并 var newArgs = Array.prototype.slice.call(arguments); return fn.apply(context, args.concat(newArgs)); } };測試動態參數
var testFn = function(obj, arg) { console.log("作用域對象屬性值:" + this.value); console.log("綁定函數時參數對象屬性值:" + obj.value); console.log("調用新函數參數值:" + arg); } var testObj = { value: 1 }; var newFn = testFn.customBind(testObj, {value: 2}); newFn("hello world"); // 執行結果: // 作用域對象屬性值:1 // 綁定函數時參數對象屬性值:2 // 調用新函數參數值:hello world
可以看出,綁定時傳的參數和新函數執行時傳的參數是合并在一起形成完整參數的。
原型鏈我們再回到bind方法的定義第二條:新函數也能使用new操作符創建對象。
說明綁定后的新函數被new實例化之后,需要繼承原函數的原型鏈方法,且綁定過程中提供的this被忽略(繼承原函數的this對象),但是參數還是會使用。所以我們需要一個中轉的函數將原型鏈傳遞下去。
首先我們需要明確new實例化過程,比如說var a = new b():
創建一個空對象a = {},并且this變量引用指向到這個空對象a;
繼承被實例化函數的原型:a.__proto__ = b.prototype;
被實例化方法b的this對象的屬性和方法將被加入到這個新的this引用的對象中:b的屬性和方法被加入的a里面;
新創建的對象由this所引用:b.call(a);
接下來我們實現原型鏈。
Function.prototype.customBind = function (context) { var self = this, args = Array.prototype.slice.call(arguments, 1); // 創建中轉函數 var cacheFn = function() {}; var newFn = function() { var newArgs = Array.prototype.slice.call(arguments); /** * 這里的 this 是指調用時的執行上下文 * 如果是 new 操作,需要綁定 new 之后作用域,this 指向新的實例對象 */ return self.apply(this instanceof cacheFn ? this : context, args.concat(newArgs)); }; // 中轉原型鏈 cacheFn.prototype = self.prototype; newFn.prototype = new cacheFn(); return newFn; };測試原型鏈
function Point(x, y) { this.x = x; this.y = y; } Point.prototype.toString = function() { return this.x + "," + this.y; }; var YAxisPoint = Point.customBind({}, 0); var axisPoint = new YAxisPoint(5); axisPoint.toString(); // "0,5" axisPoint instanceof Point; // true axisPoint instanceof YAxisPoint; // true new Point(1, 2) instanceof YAxisPoint; // true
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/108910.html
摘要:理解文章中已經比較全面的分析了在中的指向問題,用一句話來總結就是的指向一定是在執行時決定的,指向被調用函數的對象。與和直接執行原函數不同的是,返回的是一個新函數。這個新函數包裹了原函數,并且綁定了的指向為傳入的。 理解 JavaScript this 文章中已經比較全面的分析了 this 在 JavaScript 中的指向問題,用一句話來總結就是:this 的指向一定是在執行時決定的,...
摘要:秒后調用函數我有朵花瓣注意對于事件處理函數和方法也可以使用上面的方法綁定函數作為構造函數綁定函數也適用于使用操作符來構造目標函數的實例。 在討論bind()方法之前我們先來看一道題目: javascriptvar altwrite = document.write; altwrite(hello); //1.以上代碼有什么問題 //2.正確操作是怎樣的 //3.bind()方法怎么實...
摘要:文章盡量使用大量實例進行講解,它們的使用場景。在嚴格模式下,函數被調用后,里面的默認是后面通過調用函數上的和方法,該變指向,函數里面的指向。利用,可以傳入外層的上下文。同樣適用的還有,里面的對象,它也是一種類數組對象。 call,apply and bind in JavaScript 在ECMAScript中,每個函數都包含兩個繼承而來的方法:apply() 和 call(),這兩個...
摘要:簡介柯里化,又稱部分求值,是把接收多個參數的函數變成接受一個單一參數最初函數的第一個參數的函數,并且返回接受剩余的參數而且返回結果的新函數的技術。按照作者的說法,所謂柯里化就是使函數理解并處理部分應用。的思想極大地助于提升函數的復用性。 簡介 柯里化(Currying),又稱部分求值(Partial Evaluation),是把接收多個參數的函數變成接受一個單一參數(最初函數的第一個...
摘要:而模擬的方法返回的函數用作構造函數時,生成的對象為。同樣,使用運算符時,綁定構造函數和未綁定構造函數并無兩樣。標準的方法創建一個新函數稱為綁定函數,新函數與被調函數綁定函數的目標函數具有相同的函數體在規范中內置的屬性。 這是一道面試題,題目給出了使用bind方法的樣例,要求用javascript實現這個方法,面試官還很善意的提醒我函數柯里化,然而,我還是不會這道題目,所以回來這會《ja...
摘要:每個函數表達式包括函數對象括號和傳入的實參組成。和作用都是動態改變函數體內指向,只是接受參數形式不太一樣。在定義函數時,形參指定為一個對象調用函數時,將整個對象傳入函數,無需關心每個屬性的順序。 函數 JavaScript中,函數指只定義一次,但可以多次被多次執行或調用的一段JavaScript代碼。與數組類似,JavaScript中函數是特殊的對象,擁有自身屬性和方法 每個函數對象...
閱讀 1170·2021-10-20 13:48
閱讀 2195·2021-09-30 09:47
閱讀 3107·2021-09-28 09:36
閱讀 2348·2019-08-30 15:56
閱讀 1200·2019-08-30 15:52
閱讀 2025·2019-08-30 10:48
閱讀 611·2019-08-29 15:04
閱讀 573·2019-08-29 12:54