0%

认识new操作符

看了冴羽的Javscript博客,同样模拟一下new的功能实现

对于new操作符的认识

首先,是使用new的两种方式:

1
2
3
function Person(name) {this.name = name};
const person1 = new Person // person1.name === undefined
const person2 = new Person('Siro'); // person2.name === 'Siro'

其次,简单看一下new的大致流程:

执行Construct(constructor, argList),这里如果没问题的话,最终会执行constructor.[Construct](argumentsList,newTarget),所以直接看最终的抽象操作:

  1. argumentsList为参数列表(类数组),newTarget最开始没有指定的话就是constructor
  2. callerContextrunning execution context(当前运行的执行上下文)。
  3. kindF.[[ConstructorKind]]FconstructorF.[[ConstructorKind]]的可能取值为:basederived。一般定义函数的时候,这个ConstructorKind都为base
  4. 如果kindbase,则
    1. thisArgument为一个[[Prototype]]指向Constructor.prototype新对象
  5. calleeContext 为当前执行上下文栈的顶部执行上下文。这一步相当于创建了执行上下文环境等预置工作。
  6. thisArgument绑定到当前的上下文环境中。
  7. …词法环境、函数作用域的构建…
  8. 执行函数constructor,结束后,从执行上下文栈中移除calleeContext。判断constructor返回值是否为对象,如果返回值是对象,则利用这个对象作为返回值,否则使用thisArgument作为返回值。

简单说就是:

  1. 函数调用 + this的生成。
  2. 相比于函数调用,在函数返回值的时候也做了this的相应处理。
  3. this是一个[[Prototype]]指向Constructor.prorotype新对象

模拟new的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function myNew(fn, ...arguments) {
if(typeof fn !== 'function') {
throw Error('fn is not a function and can\'t not be a constructor.');
}

let thisArgument = Object.create(fn.prototype);
const result = fn.call(thisArgument, ...arguments);

if(result && (typeof result === 'object' || typeof result === 'function')) {
return result;
}else {
return thisArgument;
}
}

在创建thisArgument创建对象的时候,有一个巨大的坑,如果用:

1
2
let thisArgument = Object.create(null);  //空对象
thisArgument.__proto__ = fn.prototype;

这样做是无法关联thisArgumentfn.prototype的,由于空对象不具有原型,也不具有constructor,所以设置__proto__相当于单纯的设置属性,而不是在设置它的原型!

空对象只要用来存储数据就行了!