国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Polyfill:Function.prototype.bind的四個階段

mudiyouyou / 2893人閱讀

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

昨天邊參考es5-shim邊自己實現Function.prototype.bind,發現有不少以前忽視了的地方,這里就作為一個小總結吧。

一、Function.prototype.bind的作用

其實它就是用來靜態綁定函數執行上下文的this屬性,并且不隨函數的調用方式而變化。
示例:

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均不支持,所以才有了自己實現的需求。

三、實現: 第一階段

只要在百度搜Function.prototype.bind的實現,一般都能搜到這段代碼。

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)));
     };
   };

它能恰好的實現Function.prototype.bind的功能定義,但通過看es5-shim源碼就會發現這種方式忽略了一些細節。

第二階段

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

被忽略的細節2:函數的length屬性值是不可重寫的,使用現代瀏覽器執行下面的代碼驗證吧!

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

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

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

因此這里我們要是用eval來動態定義函數了。
具體實現:

javascriptFunction.prototype.bind = Function.prototype.bind
   || function(){
     var fn = this, presetArgs = [].slice.call(arguments); 
     var context = presetArgs.shift();
     var strOfThis = fn.toString(); // 函數反序列化,用于獲取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;         
   };

現在成功設置了函數的length屬性了。不過還有些遺漏。

第三階段

被忽視的細節4:通過Function.prototype.bind生成的構造函數。我在日常工作中沒這樣用過,不過這種情況確實需要考慮,下面我們先了解原生的Function.prototype.bind生成的構造函數的行為吧!請用現代化瀏覽器執行下面的代碼:

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屬性不會被綁定

預設實參有效

下面是具體實現

Function.prototype.bind = Function.prototype.bind
   || function(){
     var fn = this, presetArgs = [].slice.call(arguments); 
     var context = presetArgs.shift();
     var strOfThis = fn.toString(); // 函數反序列化,用于獲取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;         
   };

現在連構造函數作為使用方式都考慮到了,應該算是功德圓滿了吧!NO,上面的實現只是基礎的實現而已,并且隱藏一些bugs!
潛伏的bugs列表:

  

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

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

第四階段

針對第三階段的問題,最后得到下面的實現方式

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(); // 函數反序列化,用于獲取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的形參 // 通過函數反序列和字符串替換動態定義函數 var bound = eval("(0," + _boundStr.replace("function()", "function(" + boundArgs.join(",") + ")") + ")"); return bound; };
四、性能測試
// 分別用impl1,impl2,impl3,impl4代表上述四中實現方式
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秒

由此得知運行效率最快是第一階段的實現,而且證明通過eval動態定義函數確實耗費資源啊!!!
當然我們可以通過空間換時間的方式(Momoized技術)來緩存bind的返回值來提高性能,經測試當第四階段的實現方式加入緩存后性能測試結果為1.456,性能與第一階段的實現相當接近了。

五、本文涉及的知識點

eval的用法

new Function的用法

除new操作符外的構造函數的用法

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

Momoized技術

六、總結

在這之前從來沒想過一個Function.prototype.bind的polyfill會涉及這么多知識點,感謝es5-shim給的啟發。
我知道還會有更優雅的實現方式,歡迎大家分享出來!一起面對javascript的痛苦與快樂!

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

??

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/91524.html

相關文章

  • 關于JS函數的bind

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

    CloudwiseAPM 評論0 收藏0
  • bind 函數的使用與polyfill

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

    琛h。 評論0 收藏0
  • 手動實現bind函數(附MDN提供的Polyfill方案解析)

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

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

    摘要:返回的綁定函數也能使用操作符創建對象這種行為就像把原函數當成構造器。同時,將第一個參數以外的其他參數,作為提供給原函數的預設參數,這也是基本的顆粒化基礎。 今天想談談一道前端面試題,我做面試官的時候經常喜歡用它來考察面試者的基礎是否扎實,以及邏輯、思維能力和臨場表現,題目是:模擬實現ES5中原生bind函數。也許這道題目已經不再新鮮,部分讀者也會有思路來解答。社區上關于原生bind的研...

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

    摘要:返回的綁定函數也能使用操作符創建對象這種行為就像把原函數當成構造器。同時,將第一個參數以外的其他參數,作為提供給原函數的預設參數,這也是基本的顆粒化基礎。 今天想談談一道前端面試題,我做面試官的時候經常喜歡用它來考察面試者的基礎是否扎實,以及邏輯、思維能力和臨場表現,題目是:模擬實現ES5中原生bind函數。也許這道題目已經不再新鮮,部分讀者也會有思路來解答。社區上關于原生bind的研...

    rockswang 評論0 收藏0

發表評論

0條評論

mudiyouyou

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<