同学给我来个手写的new吧
1.手写一个new
首先看看new怎么用
function Person(name){
this.name = name
}
Person.prototype.eat = function() {
console.log("Eating")
}
var qd = new Person('qd')
console.log(qd)
qd.eat();
使用new创建的实例:
能够访问构造函数里的属性(name);
能够访问原型中的属性(eat)
在调用new时候发生了四件事:
1新生成一个对象
2将构造函数的作用域赋值给新对象(即绑定新对象的 this)
3执行构造函数中的代码(即为这个新对象添加属性)
4返回新对象
A版本
function create() {
//1.获取构造函数,并删除arguments中的第一项
var Con = [].shift.call(arguments);
//2.创建一个空对象并连接到构造函数的原型,使它能够访问原型中的属性
var obj = Object.create(Con.prototype);
3.使用apply改变构造函数中this的指向实现继承,使obj能够访问到构造函数中的属性
var ret = Con.apply(obj,arguments);
4.优先返回构造函数返回的对象
return ret instanceof Object ? ret : obj;
}
解释一下4中的判断:主要考虑的是没有返回值,
//1.有返回值且为对象:
function Person (name, sex) {
this.name = name
return {
sex:sex
}
}
1.构造函数中有返回值且为对象,那么创建的实例就只能访问到返回对象中的属性,
所以要判断一下ret的类型,如果是对象的话,则返回这个对象。
//2构造函数没有返回值:
function Person(name){
this.name = name;
}
2.构造函数没有返回值,那么创建的实例就能访问这个构造函数中的所有属性了,此时ret就会为undefined,所以返回obj。
3,返回值为基本类型
function Person(name){
this.name = name
return 'str'
}
3.构造函数中没有返回值但是返回值是undefined以外的其他基本类型(比如字符串),这种情况当成第二种情况(没有返回值)来处理。
好了我们来验证一下吧:
function Person (name) {
this.name = name
}
Person.prototype.eat = function() {
console.log("Eating")
}
function create() {
var Con = [].shift.call(arguments);
var obj = Object.create(Con.prototype);
var ret = Con.apply(obj, arguments);
return ret instanceof Object ? ret : obj;
}
//调用
var qdleader = create(Person, 'qdleader');
console.log(qdleader); //Person{name:'LinDaiDai'}
qdleader.eat() //'Eatting'
注意:object.create方法,参数为new出来的实例的原型,也就是_proto_的指向
还有其他版本的: b版本
function create() {
// 创建一个空的对象
let obj = {}
// 获得构造函数
// 因为 arguments 类数组,所以我们可以用数组的 shift 来实现 arguments 的 ‘push’ 和 ‘pop’
// 将 arguments 进数组,并将第一个元素移除并赋值给 Con (Constructor).
let Con = [].shift.call(arguments)
// 链接到原型
obj.__proto__ = Con.prototype
// 绑定this,并执行构造函数,就相当于 obj .constructor(arguments)
let result = Con.apply(obj, arguments)
// 确保 new 出来的是个对象
return typeof result === 'object' ? result : obj
}
1:arguments是一个类数组,以像是数组的样子取到传入的所有参数,有一个callee属性(值为被调用的方法)利用数组的shift方法拿到了第一项 也就是Animal函数,同时原来的arguments被更改,失去了第一个参数。再后面被apply拿来做参数
2:Constructor.apply(obj, arguments)
意思是在obj里执行构造函数,并且挨着arguments里剩下的参数
3:[ ]可以等价Array.prototype.shift ,也就是在arguments上执行数组的shift方法的意思
4:最后可以加一个判断,是否为对象再返回
比b版本更好一点的写法:
function _new() {
let target = {}; //创建的新对象
//第一个参数是构造函数
let [constructor, ...args] = [...arguments];
//执行[[原型]]连接;target 是 constructor 的实例
target.__proto__ = constructor.prototype;
//执行构造函数,将属性或方法添加到创建的空对象上
let result = constructor.apply(target, args);
if (result && (typeof (result) == "object" || typeof (result) == "function")) {
//如果构造函数执行的结构返回的是一个对象,那么返回这个对象
return result;
}
//如果构造函数返回的不是一个对象,返回创建的新对象
return target;
}
个人感觉A版本好一些
欢迎加群!