神奇的 JavaScript 之 bind.bind.bind

事情起源於一段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,難道還有瀏覽器不支持?因而去查了一下 MDNcanvas

image

好吧,果真是我大 IE8。想到 echarts 裏大量的 VML 代碼,我大百度仍是對 IE8 用戶這類瀕危物種放心不下啊。可是看這個 bind 實現又是 apply 又是 concat 又是 slice 又是 call 的,性能確定好不到哪去。瀏覽器

可是幹嗎非要讓咱們這些 Chrome 用戶爲 IE 這種糟粕付出代價呢?app

不行,非得改改不可(重度強迫症患者)。然而代碼都已經用了 bind(f, xxx) 而不是 f.bind(xxx)function bind 是逃不掉了,你只能用原生的 Function.prototype.bind 去優化這個 function bindecharts

仔細觀察這個 function bind 的用法函數

bind(f, {}, 1, 2, 3)(4, 5, 6);

與原生 Function.prototype.bind 用法性能

f.bind({}, 1, 2, 3)(4,5,6);

的區別,function bind 把原生 bindthis 做爲參數傳了,這不就是 call 嗎?因此能夠改寫爲優化

Function.prototype.bind.call(f, {}, 1, 2, 3)(4, 5, 6);

因此咱們的 function bind 就是 Function.prototype.bind.call,只不過它的 thisFunction.prototype.bind。改變一個函數的 this 須要 bind,因此就有this

var bind = Function.prototype.bind.call.bind(Function.prototype.bind);

Function.prototype.bind.call.bindcallthis 已經後面由 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

image

相關文章
相關標籤/搜索