可以说下new关键字都做了那些事情吗?

420 阅读3分钟

前言

俗话说:面向对象编程工程师,没有对象就new一个! 在上篇文章中,我们在实现 原型链继承,构造函数继承等方法中,都用到了new关键字去实例化一个对象。那么为什么new FunctionName()就能实现创建一个实例化对象呢?

探索

首先我们尝试下new FunctionName(),看得到的实例化对象都包括哪些内容。

我们借用上一篇文章中的例子:

function Student() {} 
Student.prototype.getName = function() { 
    return this.name; 
}
const student1 = new Student();
console.log(student1)

控制台执行结果:

image.png

图中能够清晰地看到,new Student();返回了一个新的对象student1, 红色方框里面返回的是student1的__proto__(补充说明:chrome的[[Prototype]]即是__proto__), 从其中的getName方法就是我们绑定到构造函数Student原型上的方法。

所以我们可以得到这样的结论:

new 一个对象的核心包括以下四步:

  1. 创建一个空对象
  2. 将空对象的__proto__指向构造函数的原型
  3. 将新对象作为构造函数的执行上下文(即this
  4. 返回这个新对象

动手实现

经过上面的一番探索分析,我们根据四步法来实现一下:

  1. 创建一个空对象 const obj = {}
  2. 将空对象的__proto__指向构造函数的原型
obj.__proto__ = FunctionName.prototype;
  1. 将新对象作为构造函数的执行上下文(即this
FunctionName.call(obj, arguments);
  1. 返回这个新对象 return obj;

完整代码示例

const MyNew = function(Fn, ...args) {
    const obj = {};
    obj.__proto__ = Fn.prototype;
    Fn.call(obj, args);
    return obj;
}

//测试一下
function Student() {} 
Student.prototype.getName = function() { 
    return this.name; 
}
// const student1 = new Student();
const student1 = MyNew(Student)
console.log(student1)

执行结果:

image.png

不能说一模一样,只能说完全一致!niceeeee!

不过还是有特殊情况没有考虑到,感谢@书生大佬。

特殊情况

  1. 构造函数的返回值是一个新对象
function Student() {
    return {test: 'hahha'}
} 
Student.prototype.getName = function() { 
    return this.name; 
}
new Student()

此时 new Student() 会返回构造函数返回的对象,如下:

image.png

  1. 构造函数返回null 当构造函数返回null时,返回的是我们内部构造的新对象,同我们上面的一般情况。

image.png


那么我们就来改造下我们的代码:

const MyNew = function(Fn, ...args) {
    const ret = Fn();
    if(Object.prototype.toString.call(ret) === "[object Object]") return ret;
    const obj = {};
    obj.__proto__ = Fn.prototype;
    Fn.call(obj, args);
    return obj;
}

//测试一下
function Student() {} 
Student.prototype.getName = function() { 
    return this.name; 
}
// const student1 = new Student();
const student1 = MyNew(Student)
console.log(student1)

测试结果:

image.png

构造函数返回null仍然会走原来的逻辑,改造完成!!!

多想一步

我们的MyNew做成了函数的形式,能不能做成类似关键字形式,直接MyNew Student()呢?

经过一番search,发现是我想多了,关键字是在语法层面定义的,js并没有开放出来,贴上思友给出的方法,供大家思考:

你可以自己写个预处理器,定义自己的语法,做完语法分析拿到AST后输出成普通js。当然这样太麻烦了,你也可以直接正则替换,类似宏的处理

具体的方法已经超出了本篇文章的探究范围,这里不做深究。

总结

关于new关键字的实现原理我们基本已经探究清楚了,大家有任何疑问都可以在评论区留言交流哦~

写文不易,大家可以动动手指点个赞,不胜感激!

相关文章

ES5 和 ES6分别是如何实现继承的?

参考资料

zhuanlan.zhihu.com/p/161677856

segmentfault.com/q/101000002…