Jiahonzheng's Blog

JavaScript new 操作符

字数统计: 847阅读时长: 3 min
2018/11/11 Share

new 原理

我们可以在 ES5 官方文档中看到其对 new 创建对象过程的定义与约束:

13.2.2 [[Construct]]

When the [[Construct]] internal method for a Function object F is called with a possibly empty list of arguments, the following steps are taken:

  1. Let obj be a newly created native ECMAScript object.
  2. Set all the internal methods of obj as specified in 8.12.
  3. Set the [[Class]] internal property of obj to "Object".
  4. Set the [[Extensible]] internal property of obj to true.
  5. Let proto be the value of calling the [[Get]] internal property of F with argument "prototype".
  6. If Type(proto) is Object, set the [[Prototype]] internal property of obj toproto.
  7. If Type(proto) is not Object, set the [[Prototype]] internal property of obj to the standard built-in Object prototype object as described in 15.2.4.
  8. Let result be the result of calling the [[Call]] internal property of F, providing obj as the this value and providing the argument list passed into [[Construct]] as args.
  9. If Type(result) is Object then return result.
  10. Return obj.

若执行 new Foo() ,其过程可简单描述为:

  1. 创建新对象 o
  2. 给新对象的内部属性赋值,关键是给 [[Prototype]] 属性赋值,构造原型链。若构造函数的原型为 Object 类型,则指向构造函数的原型,否则执行 Object 对象的原型;
  3. 内部 this 指向 o ,执行构造函数;
  4. 若构造函数显式返回对象,则返回该对象,否则返回 o

new 实现

根据上两节的内容,我们可以尝试自己实现 new

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function my_new() {
// 创建一个空的对象
let o = {};
// 获得构造函数
let constructor = [].shift.call(arguments);
// 链接到原型
let prototype =
typeof constructor.prototype === "object"
? constructor.prototype
: Object.prototype;
o.__proto__ = prototype;
// 绑定 this,执行构造函数
let result = constructor.apply(o, arguments);
// 确保 new 出来的是个对象
return typeof result === "object" ? result : o;
}

function Foo(name) {
this.name = name;
}

const obj = my_new(Foo, "Jiahonzheng");
console.log(obj); // {name: "Jiahonzheng"}

instanceof 原理

我们可以通过 instanceof 判断某个对象的类型,其内部机制是通过判断对象的原型链中是否能找到类型的 prototype

值得注意的是,null instanceof Object 返回 false ,所以 null 不是 Object 类型,尽管 typeof null 返回 "Object"

PS:typeof null 返回 "Object" 情况的出现是因为:在 JS 的最初版本中,使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000 开头代表的是对象,而 null 所有数据位为零,故 typeof 错误地将它判断为 object 。虽然现在内部类型判断代码已经改变,但是这个 Bug 仍在流传。

instanceof 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function instanceOf(left, right) {
// 获得类型的原型
let prototype = right.prototype;
// 获得对象的原型
left = left.__proto__;
// 判断对象的类型是否等于类型的原型
while (true) {
if (left === null) return false;
if (prototype === left) return true;
left = left.__proto__;
}
}

const Foo = function() {};
const o = new Foo();

console.log(instanceOf(o, Foo)); // true

// 重写 Foo 原型
Foo.prototype = {};
console.log(instanceOf(o, Foo)); // false

注意

  • 当构造函数为箭头函数时,无法使用 new 调用,原因是箭头函数无 [[Construct]] 方法。

  • 对于实例对象来说,都是通过 new 生成的。

  • 推荐使用字面量的方式创建对象,因为在使用 new Object() 的方式创建对象,需要通过作用域链层层查找到 Object ,而在使用字面量时,则无此问题。

  • new 运算符优先级问题:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function Foo() {
    return this;
    }
    Foo.func = function() {
    console.log("1");
    };
    Foo.prototype.func = function() {
    console.log("2");
    };

    new Foo.func(); // new (Foo.func()); -> 1
    new Foo().func(); // (new Foo()).func(); -> 2
CATALOG
  1. 1. new 原理
  2. 2. new 实现
  3. 3. instanceof 原理
  4. 4. instanceof 实现
  5. 5. 注意