JS 创建对象的方式
0 字面量方式
let p = {
name: 'li',
age: 12,
say: function () {
console.log(this.name);
}
}
问题:创建有同样接口的多个对象需要重复编写代码。
1 工厂模式
function createPerson(name, age) {
let o = new Object()
o.name = name
o.age = age
o.say = function () { console.log(this.name) }
return o
}
let p1 = createPerson('li', 12)
let p2 = createPerson('zhang', 13)
console.log(p1, p2)
p1.say() // li
p2.say() // zhang
工厂模式虽然可以解决多个类似对象的创建问题,但没有解决对象标识问题(即新创建的对象是什么类型)。
2 构造函数模式
function Person(name, age) {
this.name = name
this.age = age
this.say = function () {
console.log(this.name)
}
}
let p1 = new Person('li', 12)
let p2 = new Person('zhang', 13)
p1.say() // li
p2.say() // zhang
console.log(p1 instanceof Person); // true
console.log(p1 instanceof Object); // true
构造函数的问题: 其定义的方法会在每个实例上都创建一遍。
该问题可以通过将函数定义放在构造函数外面解决:
function Person(name, age) {
this.name = name
this.age = age
this.say = say
}
function say() {
console.log(this.name);
}
但是,这样虽然解决了相同逻辑的函数重复定义的问题,但是全局作用域也因此被搞乱了。如果这个对象需要多个方法,那么就要在全局作用域中定义多个函数,这就导致了自定义类型引用的代码不能很好地聚集在一起。这个问题可以通过原型模式来解决。
3 原型模式
function Person(name, age) {
this.name = name
this.age = age
}
// 在原型上定义方法
Person.prototype.say = function () {
console.log(this.name);
}
let p1 = new Person('li', 12)
let p2 = new Person('zhang', 13)
p1.say() // li
p2.say() // zhang
在原型上定义的属性和方法可以被对象实例共享。
如果在对象实例上添加了一个与原型对象中同名的属性,这个属性会覆盖原型对象上的属性。
原型模式的问题:来源于它的共享特性,原型上的所有属性在对象实例间共享,这对函数和原始值的属性来说比较合适,但是问题会产生自包含引用值的属性。
function Person() { }
Person.prototype = {
constructor: Person,
name: 'li',
age: 12,
friends: ['zhang', 'wang'],
say() {
console.log(this.name);
}
}
let p1 = new Person()
let p2 = new Person()
p1.friends.push('zhao')
console.log(p1.friends); // ["zhang", "wang", "zhao"]
console.log(p2.friends); // ["zhang", "wang", "zhao"]
console.log(p1.friends === p2.friends); // true
如果需要在多个实例间共享数组,那么则没什么问题,但一般来说不同的实例应该有属于自己的属性副本,所以就会出现问题。
解决方法:开发中不单独使用原型模式。
function Person() {
// 在构造函数中定义属性
this.friends = ['zhang', 'wang']
}
Person.prototype = {
constructor: Person,
name: 'li',
age: 12,
say() {
console.log(this.name);
}
}
let p1 = new Person()
let p2 = new Person()
p1.friends.push('zhao')
console.log(p1.friends); // ["zhang", "wang", "zhao"]
console.log(p2.friends); // ["zhang", "wang"]
console.log(p1.friends === p2.friends); // false
参考文献
[1] (美) 马特·弗里斯比(Matt Frisbie)著. 李松峰译. JavaScript高级程序设计: 第4版[M]. 人民邮电出版社, 2020.9