当我们需要创建大量的对象时,字面量创建将会导致过多的重复代码,为了解决这个问题,我们创造了一些设计模式来创建对象
创建对象
工厂模式
function createPerson(name, age, job) { var o = new Object() o.name = name o.age = age o.job = job o.sayName = function () { alert(this.name) } return o } var person1 = createPerson('Nicholas', 29, 'Software Engineer') var person2 = createPerson('Greg', 27, 'Doctor')
存在问题
- 无法解决对象识别问题(爷爷当然都是 Object 啦,但他们是拥有同一个爸爸吗?)
构造函数模式
function Person(name, age, job) { this.name = name this.age = age this.job = job this.sayName = function () { alert(this.name) } } var person1 = new Person('Nicholas', 29, 'Software Engineer') var person2 = new Person('Greg', 27, 'Doctor')
此时,person1 和 person2 的爸爸就是 Person 啦
person1 instanceof Person // true person2 instanceof Person // true
存在问题
- 同一个函数
sayName
被创建了两次,属于没有必要的内存消耗
- 同一个函数
构造函数模式 + 原型模式
function Person(name, age, job) { this.name = name this.age = age this.job = job } Person.prototype = { constructor: Person, // 不写这条的话, 实例的 constructor 为 Object sayName: function () { alert(this.name) } }
这下没毛病了吧!!有强迫症的工程师要说了,能不能把
构造函数
和原型
的代码整合到一起呢,分开后缺少一点点整体美……好吧,以下动态原型模式满足你动态原型模式
function Person(name, age, job) { this.name = name this.age = age this.job = job if (typeof this.sayName != 'function') { Person.prototype.sayName = function () { alert(this.name) } } }
继承
当我们的对象需要沿用其他对象的共同属性,又想要有自己的特点的时候,我们就需要继承了。
ECMAScript 只支持实现继承,而且其实现继承主要是依靠原型链来实现的。
——《JavaScript 高级程序设计》第 3 版
原型链
function SuperType() { this.property = true } SuperType.prototype.getSuperValue = function () { return this.property } function SubType() { this.subproperty = false } SubType.prototype = new SuperType() // 继承了 SuperType SubType.prototype.getSubValue = function () { return this.subproperty } var instance = new SubType() alert(instance.getSuperValue()) // true
存在问题
- 由于 SubType 的原型是 SuperType 的一个实例,所以所有 SubType 的实例都会共享 SuperType 的实例属性,这里共享的属性为 property,当一个 SubType 的实例修改这个属性时,其他 SubType 的实例都会受到影响
组合继承(JavaScript 中最常用的继承模式)
function SuperType(name) { this.name = name; this.colors = ['red', 'blue', 'green'] } SuperType.prototype.sayName = function () { alert(this.name); }; function SubType(name, age) { SuperType.call(this, name); this.age = age; } SubType.prototype = new SuperType(); SubType.prototype.constructor = SubType; SubType.prototype.sayAge = function () { alert(this.age); }; var instance1 = new SubType('Nicholas', 29); instance1.colors.push('black'); alert(instance1.colors); // 'red,blue,green,blank' instance1.sayName(); // 'Nicholas' instance1.sayAge(); // 29 var instance2 = new SubType('Greg', 27); alert(instance2.colors); // 'red,blue,green' instance2.sayName(); // 'Greg' instance2.sayAge(); // 27
ES6 世界的类
创建类
class Person {
constructor(name, age, job) {
this.name = name
this.age = age
this.job = job
}
sayName () {
alert(this.name)
}
}
哇,这个世界好清净!!
继承
class SuperType {
constructor(name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
sayName () {
alert(this.name);
}
}
class SubType extends SuperType {
constructor(name, age) {
super(name); // 如果不调用 super 方法,子类就得不到 this 对象
this.age = age;
}
sayAge() {
alert(this.age);
};
}
var instance1 = new SubType('Nicholas', 29);
instance1.colors.push('black');
alert(instance1.colors); // 'red,blue,green,blank'
instance1.sayName(); // 'Nicholas'
instance1.sayAge(); // 29
var instance2 = new SubType('Greg', 27);
alert(instance2.colors); // 'red,blue,green'
instance2.sayName(); // 'Greg'
instance2.sayAge(); // 27
ES6 的世界真是又清新又爽快!!
参考资料
- 《JavaScript 权威指南》第 6 版,David Flanagan 著,淘宝前端团队 译
- 《JavaScript 高级程序设计》第 3 版,Nicholas C.Zaks 著,李松峰 曹力 译
- 《ECMAScript 6 入门》,阮一峰,http://es6.ruanyifeng.com/,2018-2-2