10行代码实现JavaScript的new

131 阅读2分钟

new的使用

思考

先思考几个问题:

  1. 构造函数中this上添加的属性和方法,为什么实例有?
  2. 实例为什么可以访问原型对象上的属性和方法?
  3. new返回不同类型的值对实例有什么影响?

猜想

  1. 创建一个对象obj, 并将其原型指向构造函数fn的原型对象(原型链接)
  2. this指向obj, 执行构造函数fn
  3. 如果fn无返回值或返回基本类型,则返回obj,否则返回fn的返回值res

猜想的几点和思考部分是对应的,猜想1解决思考中问题1——原型链接;猜想2和3解决思考中的问题2(this指向obj,fn中对this的操作,不就是对obj的操作么?至于是否返回obj还得看fn返回值的类型);猜想3解决思考中问题3。具体参见以下代码注释。

验证

// 全局变量, 保存Stu中的this
const global = {}

function Stu(name, age) {
    console.log('enter', this);
    global._this = this; // 将this保存在global下,观察global._this === jack
    this.name = name;
    this.age = age;
    console.log('leave', this);
    // 以下代码验证猜想3
    // 返回基本类型——忽视
    // return name;
    // 返回引用类型——实例就是这个返回值, 此时实例没有链接到原型对象,无法使用原型对象上的showInfo方法
    // return { name, age };
}
Stu.prototype.showInfo = function() {
    console.log(`I am ${this.name}, and I am ${this.age}`);
}

// JS关键字 new
const jack = new Stu('jack', 20);

console.log(jack);
console.log(jack.__proto__ === Stu.prototype); // 验证猜想1
console.log(jack.showInfo()); // 验证猜想1
console.log(global._this === jack); // 验证猜想2

image.png

new的实现

function myNew(fn, ...args) {
    // fn不是函数,抛出异常
    if (typeof fn !== 'function') {
        const info = `expect myNew's first param to be a function but got ${typeof fn}`;
        throw new Error(info);
    }
    // 原理1:原型链接
    const obj = Object.create(fn.prototype);
    // 原理2:修改this指向,执行构造函数
    const res = fn.apply(obj, args);
    // 原理3:根据fn的返回值类型返回
    return res instanceof Object ? res : obj;
}

验证代码是否正确,只需修改const jack = new Stu('jack', 20)const jack = myNew(Stu, 'jack', 20)。修改Stu的返回值,发现myNew的表现和new一样。

new原理总结

  1. 创建一个对象obj, 并将其原型指向构造函数fn的原型对象(原型链接)
  2. this指向obj, 执行构造函数fn
  3. 如果fn无返回值或返回基本类型,则返回obj,否则返回fn的返回值res

参考: 若川:能否模拟实现JS的new操作符