# new 原理及模拟实现

# 定义

new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。 ——(来自于MDN)

举个栗子

function Car(color) {
    this.color = color;
}
Car.prototype.start = function() {
    console.log(this.color + " car start");
}

var car = new Car("black");
car.color; // 访问构造函数里的属性
// black

car.start(); // 访问原型里的属性
// black car start

可以看出 new 创建的实例有以下 2 个特性

  1. 能访问到构造函数里的属性
  2. 能访问到原型里的属性

这就有要说另一个面试经典问题:new 的过程了,(ಥ_ಥ)

# 模拟实现第一步

new 是关键词,不可以直接覆盖。这里使用 create 来模拟实现 new 的效果。

new 返回一个新对象,通过 obj.__proto__ = Con.prototype 继承构造函数的原型,同时通过 Con.apply(obj, arguments)调用父构造函数实现继承,获取构造函数上的属性;

实现代码如下

// 第一版
function create() {
	// 创建一个空的对象
    var obj = new Object(),
	// 获得构造函数,arguments中去除第一个参数
    Con = [].shift.call(arguments);
	// 链接到原型,obj 可以访问到构造函数原型中的属性
    obj.__proto__ = Con.prototype;
	// 绑定 this 实现继承,obj 可以访问到构造函数中的属性
    Con.apply(obj, arguments);
	// 返回对象
    return obj;
};

测试一下

// 测试用例
function Car(color) {
    this.color = color;
}
Car.prototype.start = function() {
    console.log(this.color + " car start");
}

var car = create(Car, "black");

car.color;      // black
car.start();    // black car start

完美!

# 模拟实现第二步

上面的代码已经实现了 80%,现在继续优化。

构造函数返回值有如下三种情况:

  1. 返回一个对象
  2. 没有 return,即返回 undefined
  3. 返回undefined以外的基本类型

情况1:返回一个对象

function Car(color, name) {
    this.color = color;
    return {
        name: name
    }
}
var car = new Car("black", "BMW");
car.color;  // undefined
car.name;   // "BMW"

实例car中只能访问到返回对象中的属性。

情况2:没有 return,即返回 undefined

function Car(color, name) {
    this.color = color;
}
var car = new Car("black", "BMW");
car.color;  // black
car.name;   // undefined

实例 car 中只能访问到构造函数中的属性,和情况1完全相反。

情况3:返回undefined以外的基本类型

function Car(color, name) {
    this.color = color;
    return "new car";
}
var car = new Car("black", "BMW");
car.color;  // black
car.name;   // undefined

实例 car 中只能访问到构造函数中的属性,和情况1完全相反,结果相当于没有返回值。

所以需要判断下返回的值是不是一个对象,如果是对象则返回这个对象,不然返回新创建的obj对象。

// 第二版
function create() {
	// 1、获得构造函数,同时删除 arguments 中第一个参数
    Con = [].shift.call(arguments);
	// 2、创建一个空的对象并链接到原型,obj 可以访问构造函数原型中的属性
    var obj = Object.create(Con.prototype);
	// 3、绑定 this 实现继承,obj 可以访问到构造函数中的属性
    var ret = Con.apply(obj, arguments);
	// 4、优先返回构造函数返回的对象
	return ret instanceof Object ? ret : obj;
};

// 第二版的代码
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;
};

# 参考