Skip to content

如何实现一个 new #20

Description

@lvwxx

new 做了什么

new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。new 关键字会进行如下操作:

  1. 创建一个空对象
  2. 设置该空对象的原型为构造函数的原型({}.protp = constructor.prototype)
  3. 执行构造函数,将 this 指向新创建的空对象
  4. 如果构造函数没有返回值或返回的不是一个对象,则返回步骤 1 新创建的对象

实现一个 new

根据以上步骤,用原生 js 实现一个 new

function _new(fn, args) {
  let obj = Object.create(fn.prototype) // 1. 创建了一个新对象,2. 新对象的 __proto__ 指向 fn.prototype
  const ret = fn.call(obj, args)  // 3. 执行构造函数,并且 this 指向新对象
  return ret instanceof Object ? ret : obj  // 4. 如果构造函数返回不是对象,则返回新对象
}

扩展

上面的代码用到了 Object.create() 和 instanceof 下面我们具体在了解一下这两个方法

Object.create

Object.create 方法创建一个新对象,并且使用参数中的对象作为新创建对象的 proto

Object.create 方法接受 2 个参数

  • proto: 新创建对象的原型对象
  • propertiesObject: 如果没有指定为 undefined,则是要添加到新创建对象的可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性描述符以及相应的属性名称。这些属性对应Object.defineProperties()的第二个参数。

那么用 Object.create 方法创建的对象,和直接用 {} 创建的对象有什么区别呢?

首先来看

let obj = {}

在 chrome 控制台中输入以上代码可以看到打印出了 obj 变量, 它有 proto 属性,这个属性指向的对象中有 toString() valueOf() 等方法

再看

let obj = Object.create({})

同样在 chrome 控制台查看, 发现它几乎和直接使用字面量创建的对象一样,不同的是,多了一层 proto 嵌套。 这是因为 Object.create 创建的对象的 proto 属性指向了参数的第一个对象。

当我们把代码修改成

let obj = Object.create({}.__proto__)

这时,obj 就和直接使用字面量形式创建的对象一样了。

我们在像 vue.js 这样的库中,经常看到 Object.create(null) 这样的代码,这是因为 null 没有 proto 属性,所有这样创建出来的对象高度干净且可定制。

instanceof

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在对象的原型链中的任何位置。所以也可用来判断一个对象是否在另一个对象的原型链中。

需要注意的是,如果表达式 obj instanceof Foo 返回true,则并不意味着该表达式会永远返回true,因为Foo.prototype属性的值有可能会改变,改变之后的值很有可能不存在于obj的原型链上,这时原表达式的值就会成为false。

Metadata

Metadata

Assignees

No one assigned

    Labels

    basicJavaScript basic knowledge must konw

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions