1.要手动实现一个 new 操作符,首先要知道 new 操作符的含义以及在使用的过程中干了什么事情,即构造函数的内部原理,简单的分为以下的四个步骤:
·创建一个新对象;
·链接到原型(将构造函数的 prototype 赋值给新对象的 __proto__);
·绑定this(构造函数中的this指向新对象并且调用构造函数);
·返回新对象;
2.这样我们就可以手动实现一个 new 方法了
const objectFactory = function(){
// 创建一个空对象
// var obj = {}; 和下面的是同样的效果
var obj = new Object();
// 获得一个构造函数
// Constructor = [].shift.apply(arguments);
Constructor = [].shift.call(arguments);
// 将构造函数的原型对象,作为新对象的原型对象
obj.__proto__ = Constructor.prototype;
// 绑定this,让this指向新的对象
// var ret = Constructor.apply(obj,arguments)
// var ret = Constructor.call(obj,arguments[0],arguments[1],arguments[2])
var ret = Constructor.call(obj,...arguments)
// 确保 使用new返回的是一个 object 对像
return typeof ret === "object" ? ret : obj;
}
3.上面我们通过创建一个对象,然后由 arguments 伪数组我们就可以知道参数包含的构造函数以及我们调用时传入的参数,然后就是对构造函数和相关参数的获取,但是 arguments 是伪数组,只拥有简单的下标访问和循环功能,并没有拥有数组的一些方法,所以我们就是用 call 和 apply 两种 方式让 arguments 伪数组拥有数组的 shift()方法。
后面又使用了call 和 apply 两种不同的绑定this的方式,call和apply的区别就是后面接的参数个数不一样。call可以接收多个参数,但是apply只能接收一个参数。但是效果是一样的
上面的 ...arguments = arguments[0],arguments[1],arguments[2] 是es6的一种更新。
3.最后我们来测试一下我们的代码的正确性
function Person (name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
// 通过手写new ,即objectFactory()方法创建的实例
const obj1 = objectFactory(Person,"cmm",18,"man")
console.log(obj1)
//Person { name: 'cmm', age: 18, sex: 'man' }
// 通过new创建的构造实例
const obj2 = new Person("cll",18,"woman");
console.log(obj2)
//Person { name: 'cll', age: 18, sex: 'woman' }
4.从上面的输出结果,可以清晰的看出我们手写的new构造器还是很成功的。