事情起源于一段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