成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Polyfill:Function.prototype.bind的四個(gè)階段

mudiyouyou / 2903人閱讀

摘要:第二階段被忽略的細(xì)節(jié)函數(shù)的屬性,用于表示函數(shù)的形參。第三階段被忽視的細(xì)節(jié)通過生成的構(gòu)造函數(shù)。五本文涉及的知識點(diǎn)的用法的用法除操作符外的構(gòu)造函數(shù)的用法下詭異的命名函數(shù)表達(dá)式技術(shù)六總結(jié)在這之前從來沒想過一個(gè)的會涉及這么多知識點(diǎn),感謝給的啟發(fā)。

昨天邊參考es5-shim邊自己實(shí)現(xiàn)Function.prototype.bind,發(fā)現(xiàn)有不少以前忽視了的地方,這里就作為一個(gè)小總結(jié)吧。

一、Function.prototype.bind的作用

其實(shí)它就是用來靜態(tài)綁定函數(shù)執(zhí)行上下文的this屬性,并且不隨函數(shù)的調(diào)用方式而變化。
示例:

javascripttest("Function.prototype.bind", function(){
   function orig(){
     return this.x;
   };
   var bound = orig.bind({x: "bind"});
   equal(bound(), "bind", "invoke directly");
   equal(bound.call({x: "call"}), "bind", "invoke by call");
   equal(bound.apply({x: "apply"}), "bind", "invoke by apply");
});
二、瀏覽器支持

Function.prototype.bind是ES5的API,所以坑爹的IE6/7/8均不支持,所以才有了自己實(shí)現(xiàn)的需求。

三、實(shí)現(xiàn): 第一階段

只要在百度搜Function.prototype.bind的實(shí)現(xiàn),一般都能搜到這段代碼。

javascriptFunction.prototype.bind = Function.prototype.bind
   || function(){
     var fn = this, presetArgs = [].slice.call(arguments); 
     var context = presetArgs.shift();
     return function(){
       return fn.apply(context, presetArgs.concat([].slice.call(arguments)));
     };
   };

它能恰好的實(shí)現(xiàn)Function.prototype.bind的功能定義,但通過看es5-shim源碼就會發(fā)現(xiàn)這種方式忽略了一些細(xì)節(jié)。

第二階段

被忽略的細(xì)節(jié)1:函數(shù)的length屬性,用于表示函數(shù)的形參。
而第一階段的實(shí)現(xiàn)方式,調(diào)用bind所返回的函數(shù)的length屬性只能為0,而實(shí)際上應(yīng)該為fn.length-presetArgs.length才對啊。所以es5-shim里面就通過bound.length=Math.max(fn.length-presetArgs.length, 0)的方式重設(shè)length屬性。

被忽略的細(xì)節(jié)2:函數(shù)的length屬性值是不可重寫的,使用現(xiàn)代瀏覽器執(zhí)行下面的代碼驗(yàn)證吧!

javascript   test("function.length is not writable", function(){
     function doStuff(){}
     ok(!Object.getOwnPropertyDescriptor(doStuff, "length").writable, "function.length is not writable");
   });

因此es5-shim中的實(shí)現(xiàn)方式是無效的。既然不能修改length的屬性值,那么在初始化時(shí)賦值總可以吧,也就是定義函數(shù)的形參個(gè)數(shù)!于是我們可通過eval和new Function的方式動(dòng)態(tài)定義函數(shù)來。
3. 被忽略的細(xì)節(jié)3:eval和new Function中代碼的執(zhí)行上下文的區(qū)別。
簡單來說在函數(shù)體中調(diào)用eval,其代碼的執(zhí)行上下文會指向當(dāng)前函數(shù)的執(zhí)行上下文;而new Function或Function中代碼的執(zhí)行上下文將一直指向全局的執(zhí)行上下文。
舉個(gè)栗子:

javascript   var x = "global";
   void function(){
     var x = "local";
     eval("console.log(x);"); // 輸出local
     (new Function("console.log(x);"))(); // 輸出global
   }();

因此這里我們要是用eval來動(dòng)態(tài)定義函數(shù)了。
具體實(shí)現(xiàn):

javascriptFunction.prototype.bind = Function.prototype.bind
   || function(){
     var fn = this, presetArgs = [].slice.call(arguments); 
     var context = presetArgs.shift();
     var strOfThis = fn.toString(); // 函數(shù)反序列化,用于獲取this的形參
     var fpsOfThis = /^function[^()]*((.*?))/i.exec(strOfThis)[1].trim().split(",");// 獲取this的形參
     var lengthOfBound = Math.max(fn.length - presetArgs.length, 0);
     var boundArgs = lengthOfBound && fpsOfThis.slice(presetArgs.length) || [];// 生成bound的形參
     eval("function bound(" 
     + boundArgs.join(",")
     + "){"
     + "return fn.apply(context, presetArgs.concat([].slice.call(arguments)));"
     + "}");
     return bound;         
   };

現(xiàn)在成功設(shè)置了函數(shù)的length屬性了。不過還有些遺漏。

第三階段

被忽視的細(xì)節(jié)4:通過Function.prototype.bind生成的構(gòu)造函數(shù)。我在日常工作中沒這樣用過,不過這種情況確實(shí)需要考慮,下面我們先了解原生的Function.prototype.bind生成的構(gòu)造函數(shù)的行為吧!請用現(xiàn)代化瀏覽器執(zhí)行下面的代碼:

test("ctor produced by native Function.prototype.bind", function(){
 var Ctor = function(x, y){
    this.x = x;
    this.y = y;
  }
 var scope = {x: "scopeX", y: "scopeY"};
 var Bound = Ctor.bind(scope);
 var ins = new Bound("insX", "insY");
 ok(ins.x === "insX" && ins.y === "insY" && scope.x === "scopeX" && scope.y === "scopeY", "no presetArgs");

  Bound = Ctor.bind(scope, "presetX");
  ins = new Bound("insY", "insOther");
  ok(ins.x === "presetX" && ins.y === "insY" && scope.x === "scopeX" && scope.y === "scopeY", "with presetArgs");
});

行為如下:

  

this屬性不會被綁定

預(yù)設(shè)實(shí)參有效

下面是具體實(shí)現(xiàn)

Function.prototype.bind = Function.prototype.bind
   || function(){
     var fn = this, presetArgs = [].slice.call(arguments); 
     var context = presetArgs.shift();
     var strOfThis = fn.toString(); // 函數(shù)反序列化,用于獲取this的形參
     var fpsOfThis = /^function[^()]*((.*?))/i.exec(strOfThis)[1].trim().split(",");// 獲取this的形參
     var lengthOfBound = Math.max(fn.length - presetArgs.length, 0);
     var boundArgs = lengthOfBound && fpsOfThis.slice(presetArgs.length) || [];// 生成bound的形參
     eval("function bound(" 
     + boundArgs.join(",")
     + "){"
     + "if (this instanceof bound){"
     + "var self = new fn();"
     + "fn.apply(self, presetArgs.concat([].slice.call(arguments)));"
     + "return self;"   
     + "}"
     + "return fn.apply(context, presetArgs.concat([].slice.call(arguments)));"
     + "}");
     return bound;         
   };

現(xiàn)在連構(gòu)造函數(shù)作為使用方式都考慮到了,應(yīng)該算是功德圓滿了吧!NO,上面的實(shí)現(xiàn)只是基礎(chǔ)的實(shí)現(xiàn)而已,并且隱藏一些bugs!
潛伏的bugs列表:

  

var self = new fn(),如果fn函數(shù)體存在實(shí)參為空則拋異常呢?

bound函數(shù)使用字符串拼接不利于修改和檢查,既不優(yōu)雅又容易長蟲。

第四階段

針對第三階段的問題,最后得到下面的實(shí)現(xiàn)方式

if(!Function.prototype.bind){
var _bound = function(){
    if (this instanceof bound){
          var ctor = function(){};
          ctor.prototype = fn.prototype;
          var self = new ctor();
          fn.apply(self, presetArgs.concat([].slice.call(arguments))); 
          return self;
        }
        return fn.apply(context, presetArgs.concat([].slice.call(arguments)));
}
, _boundStr = _bound.toString();
Function.prototype.bind = function(){
    var fn = this, presetArgs = [].slice.call(arguments);
    var context = presetArgs.shift();
var strOfThis = fn.toString(); // 函數(shù)反序列化,用于獲取this的形參 var fpsOfThis = /^function[^()]*((.*?))/i.exec(strOfThis)[1].trim().split(",");// 獲取this的形參 var lengthOfBound = Math.max(fn.length - presetArgs.length, 0); var boundArgs = lengthOfBound && fpsOfThis.slice(presetArgs.length) || [];// 生成bound的形參 // 通過函數(shù)反序列和字符串替換動(dòng)態(tài)定義函數(shù) var bound = eval("(0," + _boundStr.replace("function()", "function(" + boundArgs.join(",") + ")") + ")"); return bound; };
四、性能測試
// 分別用impl1,impl2,impl3,impl4代表上述四中實(shí)現(xiàn)方式
var start, end, orig = function(){};

start = (new Date()).getTime();
Function.prototype.bind = impl1;
for(var i = 0, len = 100000; i++ < len;){
   orig.bind({})();
}
end = (new Date()).getTime();
console.log((end-start)/1000); // 輸出1.387秒

start = (new Date()).getTime();
Function.prototype.bind = impl2;
for(var i = 0, len = 100000; i++ < len;){
   orig.bind({})();
}
end = (new Date()).getTime();
console.log((end-start)/1000); // 輸出4.013秒

start = (new Date()).getTime();
Function.prototype.bind = impl3;
for(var i = 0, len = 100000; i++ < len;){
     orig.bind({})();
}
end = (new Date()).getTime();
console.log((end-start)/1000); // 輸出4.661秒

start = (new Date()).getTime();
Function.prototype.bind = impl4;
for(var i = 0, len = 100000; i++ < len;){
    orig.bind({})();
}
end = (new Date()).getTime();
console.log((end-start)/1000); // 輸出4.485秒

由此得知運(yùn)行效率最快是第一階段的實(shí)現(xiàn),而且證明通過eval動(dòng)態(tài)定義函數(shù)確實(shí)耗費(fèi)資源?。。?!
當(dāng)然我們可以通過空間換時(shí)間的方式(Momoized技術(shù))來緩存bind的返回值來提高性能,經(jīng)測試當(dāng)?shù)谒碾A段的實(shí)現(xiàn)方式加入緩存后性能測試結(jié)果為1.456,性能與第一階段的實(shí)現(xiàn)相當(dāng)接近了。

五、本文涉及的知識點(diǎn)

eval的用法

new Function的用法

除new操作符外的構(gòu)造函數(shù)的用法

JScript(IE6/7/8)下詭異的命名函數(shù)表達(dá)式

Momoized技術(shù)

六、總結(jié)

在這之前從來沒想過一個(gè)Function.prototype.bind的polyfill會涉及這么多知識點(diǎn),感謝es5-shim給的啟發(fā)。
我知道還會有更優(yōu)雅的實(shí)現(xiàn)方式,歡迎大家分享出來!一起面對javascript的痛苦與快樂!

如果您覺得本文的內(nèi)容有趣就掃一下吧!捐贈互勉!

??

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/91524.html

相關(guān)文章

  • 關(guān)于JS函數(shù)的bind

    摘要:昨天被人問到的的作用是什么這個(gè)倒還能回答出來,之后返回一個(gè)新的函數(shù),這個(gè)函數(shù)可以保持傳遞的上下文。沒有完全實(shí)現(xiàn)規(guī)定的。比如規(guī)定了的和行為。 https://friskfly.github.io/2016/03/24/about-function-bind-in-js/ 昨天被人問到j(luò)s的bind的作用是什么? 這個(gè)倒還能回答出來,bind 之后返回一個(gè)新的函數(shù),這個(gè)函數(shù)可以保持傳遞的t...

    CloudwiseAPM 評論0 收藏0
  • bind 函數(shù)的使用與polyfill

    摘要:綁定函數(shù)被調(diào)用時(shí),也接受預(yù)設(shè)的參數(shù)提供給原函數(shù)。一個(gè)綁定函數(shù)也能使用操作符創(chuàng)建對象這種行為就像把原函數(shù)當(dāng)成構(gòu)造器。 說明 bind()方法創(chuàng)建一個(gè)新的函數(shù), 當(dāng)被調(diào)用時(shí),將其this關(guān)鍵字設(shè)置為提供的值,在調(diào)用新函數(shù)時(shí),在任何提供之前提供一個(gè)給定的參數(shù)序列。 語法 fun.bind(thisArg[, arg1[, arg2[, ...]]]) 參數(shù) thisArg 當(dāng)綁定函數(shù)被調(diào)用時(shí)...

    ?。?。 評論0 收藏0
  • 手動(dòng)實(shí)現(xiàn)bind函數(shù)(附MDN提供的Polyfill方案解析)

    摘要:被調(diào)用時(shí),等參數(shù)將置于實(shí)參之前傳遞給被綁定的方法。它返回由指定的值和初始化參數(shù)改造的原函數(shù)拷貝。一個(gè)綁定函數(shù)也能使用操作符創(chuàng)建對象這種行為就像把原函數(shù)當(dāng)成構(gòu)造器。其實(shí)這個(gè)思路也是庫如何實(shí)現(xiàn)繼承的方法。他的函數(shù)如下最后一步是將的指回。 update: 2018-06-08 原文鏈接 為什么要自己去實(shí)現(xiàn)一個(gè)bind函數(shù)? bind()函數(shù)在 ECMA-262 第五版才被加入;它可能無法在所...

    idisfkj 評論0 收藏0
  • 從一道面試題,到“我可能看了假源碼”

    摘要:返回的綁定函數(shù)也能使用操作符創(chuàng)建對象這種行為就像把原函數(shù)當(dāng)成構(gòu)造器。同時(shí),將第一個(gè)參數(shù)以外的其他參數(shù),作為提供給原函數(shù)的預(yù)設(shè)參數(shù),這也是基本的顆?;A(chǔ)。 今天想談?wù)勔坏狼岸嗣嬖囶},我做面試官的時(shí)候經(jīng)常喜歡用它來考察面試者的基礎(chǔ)是否扎實(shí),以及邏輯、思維能力和臨場表現(xiàn),題目是:模擬實(shí)現(xiàn)ES5中原生bind函數(shù)。也許這道題目已經(jīng)不再新鮮,部分讀者也會有思路來解答。社區(qū)上關(guān)于原生bind的研...

    Carson 評論0 收藏0
  • 從一道面試題,到“我可能看了假源碼”

    摘要:返回的綁定函數(shù)也能使用操作符創(chuàng)建對象這種行為就像把原函數(shù)當(dāng)成構(gòu)造器。同時(shí),將第一個(gè)參數(shù)以外的其他參數(shù),作為提供給原函數(shù)的預(yù)設(shè)參數(shù),這也是基本的顆?;A(chǔ)。 今天想談?wù)勔坏狼岸嗣嬖囶},我做面試官的時(shí)候經(jīng)常喜歡用它來考察面試者的基礎(chǔ)是否扎實(shí),以及邏輯、思維能力和臨場表現(xiàn),題目是:模擬實(shí)現(xiàn)ES5中原生bind函數(shù)。也許這道題目已經(jīng)不再新鮮,部分讀者也會有思路來解答。社區(qū)上關(guān)于原生bind的研...

    rockswang 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<