浅析new操作符原理

265 阅读2分钟

面试中我们经常会被问道new操作符的原理,有时候有笔试的时候,可能还要手写实现,今天我们就一起来简单实现一下。

首先,我们需要直到new的过程中发生了什么?我们通过一个例子看一下:

function Person(name, age) {
    this.name = name;
    this.age = age;
}

Person.prototype.say = function() {
    console.log('i am ' + this.name);
};

const p = new Person('jack', 18);

console.log(p); // output: {name: 'jack', age: 18}
p.say(); // output: i am jack

可以看到,new操作会返回一个新的对象,新的对象会继承函数Person的属性和Person的原型上的方法;接下来,我们来模拟实现一下:

// new操作符 手写模拟实现
// 1、返回一个新对象;
// 2、新对象继承构造函数的属性, 即新对象的__proto__ 等于 构造函数的prototype;
// 3、构造函数有返回值的情况,值类型和引用类型的判断;

function new1() {
    // 创建一个新对象,最后return出去
    let obj = {};
    
    // 利用数组的shift方法取到第一个参数;shift方法可以操作类数组的数据,并返回删除的第一条数据,即传入的构造函数
    const constructor = [].shift.call(arguments)
    
    // 使实例的__proto__指向构造函数的prototype
    obj.__proto__ = constructor.prototype;
    
    // 使构造函数的this指向这个新对象(也就是构造函数的实例)
    const ret = constructor.apply(obj, arguments);
    
    // 这里有个判断,如果构造函数有返回值时,判断返回值是不是对象类型,如果是则返回,如果不是则把obj返回;
    return typeof ret === 'object' ? ret : obj
}

我们再用之前的例子测试一下:

// 没有返回值的情况
function Person(name, age) {
    this.name = name;
    this.age = age;
}

Person.prototype.say = function() {
    console.log('i am ' + this.name);
};

const p = new1(Person, 'jack', 18);

console.log(p); // output: {name: 'jack', age: 18}
p.say(); // output: i am jack


// 有返回值的情况
function Person(name, age) {
    this.name = name;
    this.age = age;
    return {
        name,
        sex: 'male'
    }
}

const p = new1(Person, 'jack', 18);

console.log(p); // output: {name: 'jack', sex: 'male'}

经验证,我们模拟的new1方法能简单实现new操作符的结果。