简述
传统的 javascript 中只有对象,没有类的概念。它是基于原型的面向对象语言。原型对象特点就是将自身的属性共享给新对象。
先看一下 new 关键字是他都做了什么,让我们通过 new 实现继承的。
下面请看代码:
1 | function Car(name, year, model) { |
new 关键字进行如下的操作:
- 创建一个空的 JavaScript 对象;
- 链接该对象(即设置该对象的构造函数)到另一个对象;
- 将步骤 1 新创建的对象作为 this 的上下文;
- 如果该函数没有返回对象,则返回 this。
同时,我们自己写的这个函数接收的第一个参数就是我们要继承的对象。
下面我们就一步一步实现一个自己的 new 关键字
第一步
第一步比较简单我们要首先定义一个 create 方法在方法内创建一个空对象
1 | function create() { |
这个对象会在后面用到,经过后面的处理,如果没有返回值,就会返回我们创建的这个空对象。
第二步
第二步比较关键,用到了我们基于 prototype 继承的知识。就是把我们新创建的这个空对象的proto,指向我们要继承对象的prototype。
下面我们就在第一步代码的基础上实现
1 | function create() { |
在这一步我们就是通过proto关联了我们创建的空对象的 prototype 到我们要继承的另一个对象。
第三步
第三步,将步骤 1 新创建的对象作为this 的上下文,我们通过apply 执行构造函数并且改变 this 指向。
下面我们在第二步的基础上实现
1 | function create() { |
第四步
判断是否有返回值,如果该函数没有返回对象,则返回 this。
1 | function create() { |
测试
1 | function create() { |
一道面试题
一道有关 new 的面试题,其实一部还是考察运算符优先级。
1 | function Foo() { |
其实这个是考察运算优先级和 new 的面试题,通过 mdn 上的运算优先级
new Foo() 的优先级大于 new Foo ,所以对于上述代码来说可以这样划分执行顺序
1 | new (Foo.getName())(); |
对于第一个函数来说,先执行了 Foo.getName() ,所以结果为 1;对于后者来说,先执行 new Foo() 产生了一个实例,然后通过原型链找到了 Foo 上的 getName 函数,所以结果为 2。