最近了解 ES6 的箭头函数时无意间扯出了一连串的问题。首先碰到了 bind() 函数,先前只了解call() 和 apply() 不曾注意到 bind(),于是视线暂时转移到 bind()。
当搜索 MDN 时发现 bind() 根据参数个数的不同分为不同的功能,其中手册中的例子在演示传入两个参数时第一个参数传入 undefined 这让我想起来了在 jQuery 框架中也曾经见到这样的做法但一直懒惰不去仔细了解,于是这个问题链的终点就停在此,我将以“递归”的方式从前往后发现问题,从后往前解决问题。
jQuery 为什么将 undefined 当作参数传入?
防止 window.undefined 被重写,由于 undefined 不是 JS 的保留字,所以存在开发者在全局作用域声明一个名为 undefined 的变量并赋值的情况。当开发者自己编写的函数内部用到了 window.undefined 在寻找时追溯到顶层作用域而此时发现已被重写,就会导致意外发生。值得注意的是这个问题只存在于一些老的浏览器,当时的规则 undefined 是可以被重写的。而当 ES 5 出台后规定全局作用域中的 window.undefined 是不能被写的,而函数作用域的依旧可以。
jQuery 为了兼容旧浏览器的做法
1 | (function(window,undefined){ |
这种解决方案的关键点在于设定 undefined 形参,但不传入对应的实参。这样可以保证将 window.undefined 传入函数内部。
。
这里有个知识点:JS 中缺省的参数,默认传递 undefined。
当函数内部需要用到 undefined 时会先在内部作用域查找,而此时 undefined 已被传递进来并且是被赋为 window.undefined ,所以就停止了向上查找,排除了 window.undefined 被重写造成的影响。这种情况只存在于老旧浏览器, window.undefined 可被重写的 bug 。随着时间的发展,这种写法已不再使用,因为没有必要再兼容老旧浏览器
bind() 函数:两套参数,两种功能
bind()方法会创建一个新函数。当这个新函数被调用时,bind()的*第一个参数将作为它运行时的 this, 之后的一序列参数将会在传递的实参前传入作为它的参数。 引自 MDN
一个参数:创建一个函数,使这个函数不论怎么调用都有同样的 this 值。
1 | this.x = 9; |
两个参数:使一个函数拥有预设的初始参数
1 | function list() { |
手写 bind()
1 | function bind(fn, context){ |
上述代码包含两个知识点:
- Array.prototype.slice.call(arguments) 可以将一个具有 length 属性的对象转化为数组
- 当需要传的实参数量少于形参的数量时,可以用 undefined 占位。