共计 1410 个字符,预计需要花费 4 分钟才能阅读完成。
事件起源于一段 JS 代码:
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 的源码,于是就发现了这段代码。
干嘛要本人从新实现一遍 bind
,难道还有浏览器不反对?于是去查了一下 MDN:
好吧,果然是我大 IE8。想到 echarts 里大量的 VML 代码,我大百度还是对 IE8 用户这类濒危物种放心不下啊。然而看这个 bind 实现又是 apply 又是 concat 又是 slice 又是 call 的,性能必定好不到哪去。
然而干嘛非要让咱们这些 Chrome 用户为 IE 这种糟粕付出代价呢?
不行,非得改改不可(重度强迫症患者)。然而代码都曾经用了 bind(f, xxx)
而不是 f.bind(xxx)
。function bind
是逃不掉了,你只能用原生的 Function.prototype.bind
去优化这个 function bind
仔细观察这个 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
,所以就有
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