写在前边:
手写一个 new 咱们须要理解原型的概念,和 this的指向问题。
在执行 new 的时候做了什么
咱们执行 new 来看一下 new 的过程中产生了什么:
function Person(name) {
this.name = name
this.sayName = function () {console.log('name', this.name)
}
}
const person = new Person('ayetongzhi')
console.log('person', person)
person.sayName()
打印后果如上,依据打印后果咱们能够晓得:
- 返回了一个对象(person),这个对象不会凭空的产生,因而在 new 的过程中是创立了一个对象并返回。
- 将 this 指向创立的新对象,并执行函数。须要将 this 指向新的对象,能力构造函数的属性赋值给实例对象。
- 扭转实例 的 \_\_proto\_\_ 属性,指向构造函数的原型。如下图所示,通过构造函数 (Function) 创立的实例 (instance),它的 \_\_proto\_\_ 属性指向构造函数 (Function) 的 prototype。
- 构造函数显示的返回一个对象,则返回该对象。上面对这条问题进行解释,上代码:
function Person(name) {
this.name = name;
this.sayName = function () {console.log("name", this.name);
};
// 显示的返回一个对象
return {
name: "maomao",
sayName: function () {console.log("name", this.name);
}
};
}
const person = new Person("ayetongzhi");
console.log("person", person);
person.sayName(); //maomao
运行后果可知,在 new Person 操作后返回了 return 前面返回的对象。
实现 new
依据 new 操作执行的内容,来一步步实现手写的 new。
// fn 构造函数
// ...args 构造函数传入的参数
function myNew (fn, ...args) {
// 创立新的对象
var obj = {}
// 将对象的 __proto__ 属性指向构造函数的原型
obj.__proto__ = fn.prototype
// 将 **this** 指向创立的新对象,并执行构造函数,保留返回后果
let result = fn.call(obj, ...args)
// 构造函数返回的后果是对象则返回该对象 result,否则返回新创建的对象 obj
return result instanceof Object ? result : obj
}
通过以上代码咱们实现了手写的 new,然而 \_\_proto\_\_ 属性是一个有争议不倡议应用的属性,咱们应用 Object.create() 办法来代替。Object.create() 办法用于创立一个新对象,应用现有的对象来作为新创建对象的原型(prototype)。举个例子:
let a = {name: 'maomao'}
let b = Object.create(a)
console.log('b 的 __proto__ 指向 a', b.__proto__ === a) // true
以上的代码 let b = Object.create(a) 使新创建的对象 b 的 \_\_proto\_\_ 指向了 现有对象a。
因而,咱们优化一下手写的 myNew 办法:
// fn 构造函数
// ...args 构造函数传入的参数
function myNew (fn, ...args) {
// 创立新对象 obj,并扭转 obj 的 __proto__ 指向构造函数的 prototype
var obj = Object.create(myNew.prototype)
// 将 **this** 指向创立的新对象,并执行构造函数,保留返回后果
let result = fn.call(obj, ...args)
// 构造函数返回的后果是对象则返回该对象 result,否则返回新创建的对象 obj
return result instanceof Object ? result : obj
}
运行以下代码测验手写的 new 办法是否存在问题:
function Person(name) {
this.name = name;
this.sayName = function () {console.log("name", this.name);
};
}
const person = myNew(Person, "ayetongzhi");
console.log("person", person);
person.sayName(); // ayetongzhi
当没有显示的返回对象的时候,新创建了对象。并扭转 this 指向到新的对象,同时执行构造函数。扭转了新创建对象的 \_\_proto\_\_ 指向了构造函数的 prototype,与 new 操作符执行的后果统一。。
function Person(name) {
this.name = name;
this.sayName = function () {console.log("name", this.name);
};
return {
name: "maomao",
sayName: function () {console.log("name", this.name);
}
};
}
const person = myNew(Person, "ayetongzhi");
console.log("person", person);
person.sayName(); // maomao
当构造函数显示的返回一个对象的时候,打印后果如下,与 new 操作符执行的后果统一。
以上咱们理解了在执行 new 操作时,做了些什么,并依据 new 操作的体现,实现了手写 new 函数。