JavaScript温故而知新——new操作符的实现

853 阅读2分钟

首先我们要知道new 操作符具体做了些什么?

  1. 创建一个空对象
  2. 然后让这个空对象的__proto__指向函数的原型prototype
  3. 执行构造函数中的代码,构造函数中的this指向该对象
  4. 如果构造函数有返回值,则以该对象作为返回值。若没有returnreturn了基本类型,则将新对象作为返回值

如果对于原型还不是很了解的话建议先看看:JavaScript温故而知新——原型和原型链

先来实现前三点的效果:

function new2() {
    // 新建一个对象
    var obj = new Object(),
    
    // 取得第一个参数,即传入的构造函数。
    // 这里 shift 会修改原数组,因此 arguments 会被去掉第一个参数
    Constructor = [].shift.call(arguments);
    
    // 将 obj 的原型指向构造函数,这样 obj 就可以访问到构造函数原型中的属性了
    obj.__proto__ = Constructor.prototype;
    
    // 使用 apply ,改变构造函数 this 的指向到新建的对象,这样 obj 就可以的访问到构造函数中的属性了
    Constructor.apply(obj, arguments);
    return obj;
};

我们可以做个测试

function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.sayName = function() {
    console.log('I am' + this.name);
}
var person = new2(Person, 'xiao', '18')

console.log(person.name)    // xiao
console.log(person.age)     // 18
person.sayName();           // I am xiao

实现返回值的效果

假如构造函数是有返回值,并且返回值是一个对象:

function Person(name, age) {
    this.skill = 'eat';
    this.age = age;
    return {
        name: name,
        habit: 'sleep'
    }
}
var person = new Person('xiao', '18')

console.log(person.name)    // xiao
console.log(person.habit)     // sleep
console.log(person.skill)    // undefined
console.log(person.age)     //  undefined

可以看到实例person中只能访问返回的对象中的属性,说明此时new的返回值为构造函数Person return 出来的对象。

假如构造函数的返回值是一个基本类型的值:

function Person(name, age) {
    this.skill = 'eat';
    this.age = age;
    return 'hello world'
}
var person = new Person('xiao', '18')

console.log(person.name)    // undefined
console.log(person.habit)     // undefined
console.log(person.skill)    // eat
console.log(person.age)     //  18

可以看到实例person可以正常访问构造函数中的属性,说明此时new的返回值为新创建的原型指向构造函数的对象。

按照上面的结论来完整实现我们的new2

function objectFactory() {

    var obj = new Object(),

    Constructor = [].shift.call(arguments);

    obj.__proto__ = Constructor.prototype;

    var ret = Constructor.apply(obj, arguments);

    // 判断构造函数返回值是否为对象,是的话返回这个对象,不是则正常返回
    return typeof ret === 'object' ? ret : obj;

};

结尾

系列文章: