简介new

new 运算符是创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例,其创建过程如下:

  • 创建一个空的简单JavaScript对象(即{})
  • 链接该对象(即设置该对象的构造函数)到另一个对象
  • 将第一步新创建的对象作为this的上下文
  • 如果该函数没有返回对象,则返回this

举个栗子:

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

Person.prototype.sex = 'female';

Person.prototype.sayHello = function(){
    console.log('Hello!My name is ' + this.name);
}

var person = new Person('Youxuan','20');

console.log(person.name);   //Youxuan
console.log(person.habit);  //swim
console.log(person.sex);    //female
person.sayHello();  //Hello!My name is Youxuan

从上述内容中,可以看出实例访问到Person构造函数里的属性,
也可以访问到Person.prototype里的属性;
因此,我们可以尝试模拟一下new的实现方法,先来回顾一下new是如何使用的

function Person(){
    ...
}
var person = new Person(...);
var person = newObject(...);

初步模拟

因为new的结果是一个对象,所以在实现new的过程中,也要生成一个新的对象。我们先将这个对象命名为obj,因为obj也具有Person构造函数的属性,所有要通过调用父类,继承父类实例的方式,来给obj添加新属性。

回顾上一篇文章,我们讲解了原型和原型链,把obj的proto指向构造函数Person的原型对象prototype,此时便建立了obj对象的原型链:

obj->Person.prototype->Object.prototype->null,实例以此关系来访问原型上的属性

function newObject(){
    var obj = new Object();
    Constructor = [].shift.call(arguments);
    obj.__proto__ = Constructor.prototype;
    Constructor.apply(obj,arguments);
    return obj;
}

在以上内容中,通过new Object()创建了一个对象obj。

取出第一个参数,就是我们要传入的构造函数。因为shift会修改原数组,所以arguments会被去除第一个参数。

将obj的原型指向构造函数,这样obj就可以访问到构造函数原型中的属性。

利用apply,改变构造函数this指向到新建对象,这样obj就能访问到构造函数中的属性,然后返回obj。

现在,我们写一段代码来验证一下效果:

function Person(name,age){
    this.sex = 'female';
    this.age = age;
    return {
        name: name,
        habit: 'swim'
    }
}
var person = new Person('Youxuan','20');
console.log(person.name);   //Youxuan
console.log(person.habit);  //swim
console.log(person.sex);    //undefined
console.log(person.age);    //undefined

以上代码中,构造函数返回了一个对象,在实例person中只能访问返回的对象中的属性。

而且另需注意,这里返回的是一个对象,假若需要返回一个基本类型的数据时该怎么办呢?

function Person(name,age){
    this.sex = 'female';
    this.age = age;
    return 'Beauty on my face'
}
var person = new Person('Youxuan','20');
console.log(person.name);       //undefined
console.log(person.habit);      //undefined
console.log(person.sex);        //female
console.log(person.age);        //20

这时结果跟上次完全相反,尽管这次有返回值,但是相当于没有对返回值进行处理。

效果实现

所以仍需判断返回值是不是一个对象,如果是一个对象,就返回这个对象;如果不是一个对象,那就将它直接返回:

function newObject(){
    var obj = new Object();
    Constructor = [].shift.call(arguments);
    obj.__proto__ = Constructor.prototype;
    var demo = Constructor.apply(obj,arguments);
    return typeof demo === 'object' ? demo : obj;
}

至此就模拟实现了new操作符的效果,主要原理就是原型链apply继承。