事情起源於一段JS代碼:git
function bind(func, context) { var args = nativeSlice.call(arguments, 2); return function () { return func.apply(context, args.concat(nativeSlice.call(arguments))); }; }
源代碼出自 echarts 的底層依賴 zrender,爲了優化個人 offscreen-echarts 庫我在讀 echarts 的源碼,因而就發現了這段代碼。github
幹嗎要本身從新實現一遍 bind
,難道還有瀏覽器不支持?因而去查了一下 MDN:canvas
好吧,果真是我大 IE8。想到 echarts 裏大量的 VML 代碼,我大百度仍是對 IE8 用戶這類瀕危物種放心不下啊。可是看這個 bind 實現又是 apply 又是 concat 又是 slice 又是 call 的,性能確定好不到哪去。瀏覽器
可是幹嗎非要讓咱們這些 Chrome 用戶爲 IE 這種糟粕付出代價呢?app
不行,非得改改不可(重度強迫症患者)。然而代碼都已經用了 bind(f, xxx)
而不是 f.bind(xxx)
。function bind
是逃不掉了,你只能用原生的 Function.prototype.bind
去優化這個 function bind
echarts
仔細觀察這個 function bind
的用法函數
bind(f, {}, 1, 2, 3)(4, 5, 6);
與原生 Function.prototype.bind
用法性能
f.bind({}, 1, 2, 3)(4,5,6);
的區別,function bind
把原生 bind
的 this
做爲參數傳了,這不就是 call
嗎?因此能夠改寫爲優化
Function.prototype.bind.call(f, {}, 1, 2, 3)(4, 5, 6);
因此咱們的 function bind
就是 Function.prototype.bind.call
,只不過它的 this
是 Function.prototype.bind
。改變一個函數的 this
須要 bind
,因此就有this
var bind = Function.prototype.bind.call.bind(Function.prototype.bind);
Function.prototype.bind.call.bind
中 call
的 this
已經後面由 bind
指定了,call
前面的 this
已經失去了意義,因此其等價於
var bind = Function.prototype.call.bind(Function.prototype.bind);
咱們須要在瀏覽器支持原生 bind
的前提下用新實現覆蓋原始的 function bind
,因此改寫爲
if (bind.bind) { bind = bind.call.bind(bind.bind); }
bind.bind === Function.prototype.bind
,和 [].slice === Array.prototype.slice
一個意思。
這裏其實已是最佳實現了,由於只是把原生 bind
使用 call
方法調用,性能幾乎等同於原生 bind
的性能。若是把參數 bind.bind
移到函數前面再 bind
一層,就變成
if (bind.bind) { bind = bind.bind.bind(bind.call); }
因而就出現了 bind.bind.bind
。可是由於這裏其實有兩層 bind
因此實際性能是有損耗的:https://jsben.ch/sEcop