关于JavaScript中new运算符的一些理解

最近看到一道很有意思的面试题,问输出是多少:

var name = "global";
var obj = {
  name: "local",
  foo: function () {
    this.name = "foo";
  }.bind(window)
};
var bar = new obj.foo();
setTimeout(function () {
  console.log(window.name);
}, 0);
console.log(bar.name);

var bar3 = bar2 = bar;
bar2.name = "foo2";
console.log(bar3.name);

我直呼刁钻,这个里面考的东西属实有点多,运算符和优先级、this的指向和绑定优先级、事件队列、new运算符……不过这里我打算说的是new的事情,并不打算在别的地方纠缠,所以不会在非关键点进行解释,还请读者包涵。

首先,三条语句的执行顺序是很明显的:

console.log(bar.name);    // foo
console.log(bar3.name);   // foo2
console.log(window.name); // global

那么就牵扯到一个问题,也是这里的关键问题:bar是什么?我们先看看new的调用过程(来自于MDN,由本人翻译):

  1. 创建一个空的简单JavaScript对象(即{});
  2. 设置该对象的constructor属性值,指向它的构造函数;
  3. 将步骤1新创建的对象作为this的上下文 ;
  4. 如果该函数没有返回对象,则返回this

所以,毫无疑问,这个bar应该是这样一个对象:

{ name: "foo" }

但是,按理来说,此处已经使用了bind将this绑定到了window,调用时的this.name = "foo";应该会修改window的name属性值。但由于绑定优先级,new运算符的优先级是高于bind的,所以此处的bind导致的this修改是被覆盖的。至于这一点,可以通过下面的代码验证(其实跟题目里的场景很类似):

function foo(something) {
  this.a = something;
}

var obj1 = {};

var bar = foo.bind(obj1);
bar(2);
console.log(obj1.a); // 2

var baz = new bar(3);
console.log(obj1.a); // 2
console.log(baz.a); // 3

但是,为什么会引起覆盖呢?因为我们并不太能深入具体的native code(比如V8的具体实现),所以用MDN上的bind函数的polyfill来举例:

if (!Function.prototype.bind)
  (function () {
    var ArrayPrototypeSlice = Array.prototype.slice;
    Function.prototype.bind = function (otherThis) {
      if (typeof this !== "function") {
        // closest thing possible to the ECMAScript 5
        // internal IsCallable function
        throw new TypeError(
          "Function.prototype.bind - what is trying to be bound is not callable"
        );
      }

      var baseArgs = ArrayPrototypeSlice.call(arguments, 1),
        baseArgsLength = baseArgs.length,
        fToBind = this,
        fNOP = function () {},
        fBound = function () {
          baseArgs.length = baseArgsLength; // reset to default base arguments
          baseArgs.push.apply(baseArgs, arguments);
          return fToBind.apply(
            fNOP.prototype.isPrototypeOf(this) ? this : otherThis,
            baseArgs
          );
        };

      if (this.prototype) {
        // Function.prototype doesn't have a prototype property
        fNOP.prototype = this.prototype;
      }
      fBound.prototype = new fNOP();

      return fBound;
    };
  })();

重点其实是这一句:

fNOP.prototype.isPrototypeOf(this) ? this : otherThis

言下之意,如果是通过new关键字创建的,this指向并不会改变。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值