在007-js创建对象的几种方法博客中,我们使用构造函数创建了对象,但是这里有个问题,new到底做了什么呢,导致构造函数模式和工厂模式不一样呢。
我们借用《JavaScript高级程序设计》中的定义:要创建Person的新实例,必须使用new操作符,以这种方式调用构造函数实际上会经历以下4个步骤:
- 创建一个新对象
- 将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
- 执行构造函数中的代码(为这个新对象添加属性)
- 返回新对象
我们来用代码实现
1.实现第一步和第四步
由于new是关键字,所以我们使用myNew函数来模拟实现。
function myNew() {
var o = new Object();
return o;
}
是不是和工厂模式很像
2实现第二步和第三步
我们调用的时候要使用myNew(Person, 'James', 20)
方式调用,所以我们要用arguments
来取出函数中的参数
function myNew() {
var o = new Object(); // 第一步:创建一个新对象
var param = [].shift.call(arguments); // 拿到第一个参数Person,也就是构造函数
param.apply(o, arguments); // 第二步:将构造函数的作用域赋给新对象(因此this就指向了这个新对象)和第三步:执行构造函数中的代码(为这个新对象添加属性)
return o; // 第四步:返回新对象
}
function Person(name, age) {
this.name = name;
this.age = age;
this.say = function() {
console.log(this.name);
}
}
myNew(Person, 'James', 20).say(); // 'James'
这样我们已经按照步骤模拟实现了一个myNew方法,但是我们在原型上添加方法,就会报错了,加上以下代码尝试一下
Person.prototype.sayAge = function() {
console.log(this.age);
}
myNew(Person, 'James', 20).sayAge(); // myNew(...).sayAge is not a function
所以,我们需要一个连接到原型的操作,由
008-JS原型和原型链(1)的第四张图片可知,person.__proto__ === Person.prototype
,所以最终实现版本是
function myNew() {
var o = new Object(); // 第一步:创建一个新对象
var param = [].shift.call(arguments); // 拿到第一个参数Person,也就是构造函数
// 新增代码
// o.__proto__ = param.prototype;
Object.setPrototypeOf(o, param.prototype); // 连接实例和原型,上面注释掉的方法效果跟这个一样
param.apply(o, arguments); // 第三步:将构造函数的作用域赋给新对象(因此this就指向了这个新对象)和第四步:执行构造函数中的代码(为这个新对象添加属性)
return o; // 第四步:返回新对象
}
function Person(name, age) {
this.name = name;
this.age = age;
this.say = function() {
console.log(this.name);
}
}
Person.prototype.sayAge = function() {
console.log(this.age);
}
myNew(Person, 'James', 20).say(); // 'James'
myNew(Person, 'James', 20).sayAge(); // 20
参考:
《JavaScript高级程序设计》
https://juejin.im/post/590a99015c497d005852cf26