很显然,默认绑定 在四种规则中优先权最低的。所以我们先把它放在一边。

    隐含绑定明确绑定 哪一个更优先呢?我们来测试一下:

    所以, 明确绑定 的优先权要高于 隐含绑定,这意味着你应当在考察 隐含绑定 之前 首先 考察 明确绑定 是否适用。

    现在,我们只需要搞清楚 new 绑定 的优先级位于何处。

    1. function foo(something) {
    2. this.a = something;
    3. }
    4. var obj1 = {
    5. foo: foo
    6. };
    7. var obj2 = {};
    8. console.log( obj1.a ); // 2
    9. obj1.foo.call( obj2, 3 );
    10. console.log( obj2.a ); // 3
    11. var bar = new obj1.foo( 4 );
    12. console.log( obj1.a ); // 2
    13. console.log( bar.a ); // 4

    好了,new 绑定 的优先级要高于 隐含绑定。那么你觉得 new 绑定 的优先级较之于 明确绑定 是高还是低呢?

    注意: newcall/apply 不能同时使用,所以 new foo.call(obj1) 是不允许的,也就是不能直接对比测试 new 绑定明确绑定。但是我们依然可以使用 硬绑定 来测试这两个规则的优先级。

    在我们进入代码中探索之前,回想一下 硬绑定 物理上是如何工作的,也就是 Function.prototype.bind(..) 创建了一个新的包装函数,这个函数被硬编码为忽略它自己的 this 绑定(不管它是什么),转而手动使用我们提供的。

    因此,这似乎看起来很明显,硬绑定明确绑定的一种)的优先级要比 new 绑定 高,而且不能被 new 覆盖。

    我们检验一下:

    如果你回头看看我们的“山寨”绑定帮助函数,这很令人吃惊:

    1. function bind(fn, obj) {
    2. return function() {
    3. fn.apply( obj, arguments );
    4. }

    如果你推导这段帮助代码如何工作,会发现对于 new 操作符调用来说没有办法去像我们观察到的那样,将绑定到 obj 的硬绑定覆盖。

    但是 ES5 的内建 Function.prototype.bind(..) 更加精妙,实际上十分精妙。这里是 MDN 网页上为 bind(..) 提供的(稍稍格式化后的)polyfill(低版本兼容填补工具):

    注意: 就将与 new 一起使用的硬绑定函数(参照下面来看为什么这有用)而言,上面的 bind(..) polyfill 与 ES5 中内建的 bind(..) 是不同的。因为 polyfill 不能像内建工具那样,没有 .prototype 就能创建函数,这里使用了一些微妙而间接的方法来近似模拟相同的行为。如果你打算将硬绑定函数和 new 一起使用而且依赖于这个 polyfill,应当多加小心。

    允许 new 进行覆盖的部分是这里:

    1. this instanceof fNOP &&
    2. oThis ? this : oThis
    3. fNOP.prototype = this.prototype;
    4. fBound.prototype = new fNOP();

    我们不会实际深入解释这个花招儿是如何工作的(这很复杂而且超出了我们当前的讨论范围),但实质上这个工具判断硬绑定函数是否是通过 new 被调用的(导致一个新构建的对象作为它的 this),如果是,它就用那个新构建的 this 而非先前为 this 指定的 硬绑定

    为什么 new 可以覆盖 硬绑定 这件事很有用?

    这种行为的主要原因是,创建一个实质上忽略 this硬绑定 而预先设置一部分或所有的参数的函数(这个函数可以与 new 一起使用来构建对象)。bind(..) 的一个能力是,任何在第一个 this 绑定参数之后被传入的参数,默认地作为当前函数的标准参数(技术上这称为“局部应用(partial application)”,是一种“柯里化(currying)”)。

    例如:

    1. 函数是通过 new 被调用的吗(new 绑定)?如果是,this 就是新构建的对象。

      var bar = new foo()

    2. 函数是通过 callapply 被调用(明确绑定),甚至是隐藏在 bind 硬绑定 之中吗?如果是,this 就是那个被明确指定的对象。

      var bar = foo.call( obj2 )

    3. 函数是通过环境对象(也称为拥有者或容器对象)被调用的吗(隐含绑定)?如果是,this 就是那个环境对象。

      var bar = obj1.foo()

    以上,就是理解对于普通的函数调用来说的 this 绑定规则 所需的全部。是的……几乎是全部。