New操作符的机制与实现相同功能的方法
大纲
new操作符做了什么?
先来看一个例子,分别用新旧方式创建类,并实例化。
1 | // 传统实现 |
分别创建两个类的实例
1 | const foo = new Foo('isaac'); |
首先,可以看输出结果。很明显两个实例的结构上是基本一致的,类比一下,你可以猜到新语法“class”的实现本质,当然class也确实是传统实现的语法糖。
所以,接下来直接使用Foo的定义方式作为例子讲。
再来看看定义一个类的时候我们做了什么:
- 定义了一个构造函数,将需要属性赋值给了上下文(context,即上文的this)
- 定义了构造函数的属性prototype,将方法、属性挂载在这个prototype上。
最后再对比下实例的结构:
你可以发现,传入Foo的实参被初始化到了对象的第一层属性上:
说不准这就是构造函数做的事情:通过构造函数将传参,经过逻辑处理,然后初始化到实例上。
在看输出结果,在name
属性的下面还有一个__proto__
属性,他是一个类对象的结构,你可以发现有两个成员是我们自己定义的:
- fn
- sayName
这两个属性都是我们在定义类的时候挂载到prototype上的。
说不准这就是我们定义在Foo.prototype属性
最后,我们根据上面两个猜想,再进行推断new做了什么事情:
- new通过构造函数创建了一个对象foo,并且通过构造函数的逻辑初始化了这个对象的基本成员;
- new将foo的原型引用指向了Foo.prototype,从而连接好从
foo -> Foo.prototype -> Object.prototype
这条原型链。
上面即是猜想也是实质,我不去证明,这不是我的主要想写的,我是想根据这个猜测去实现一个方法达到“new”的功能。
小结一下
javascript的类的实现分成两部分:
- Constructor(构造函数)
- Proptotype(原型)
实例化一个类的实例的本质是:
- 创建一个对象,且在访问该对象成员时,可以向它类指向原型回溯;
- 对象的成员属性(包含方法)通过Constructor进行初始化;
实现一个new创建实例
分析
根据上面对new操作符的分析,我们可以这么做:
- 创建一个对象;
- 将这个对象的原型引用(不是这个对象的原型,是一个内部属性
__proto__
指向); - 调用构造函数,将上下文(this)绑定到对象。
具体实现
- 使用
setPrototypeOf
实现原型指向
setPrototypeOf
是ES6的新方法,具体实现其实就是对对象__proto__
的修改
1 | function setPrototypeOf(_ctx, _proto) { |
1 | /** |
- 通过
Object.create
“继承”类
Object.create
是官方对原型式继承的内部实现,具体逻辑如下:
1 | function objectCreate(prototype) { |
对于原型式继承可参考: 原型式继承
Object.create
不但创建了一个对象,并且设置了这个对象的__proto__
指向。
1 | function newInsOf( |