Javascript 构造函数模式、原型模式
前两天写完组合继承,打算总结一下原型继承的,不过今天看了一下工厂模式、构造函数模式和原型模式,觉得有必要总结一下以加深印象。
——————————————————————————————————————————————————————————————————————————————————碎碎念。
1.工厂模式
《Javascript 高级程序设计(第3版)》 用了寥寥十多行介绍了工厂模式。我找了一些相关资料,想确定一下这种模式的具体适用场景和优势。按照资料中的说法,是考虑到 ECMAScript 无法创建类,所以:
创建一个对象,紧接着描述对象的属性和方法,最后用另一个对象把它们封装起来当作接口。
按照上面描述的,工厂函数是用来在 Javascript 中实现类似于 Java 中类的功能。通过调用工厂函数,可以创建多个相似的对象实例。
简单工厂模式:使用一个类(通常为单体)来生成实例。
复杂工厂模式:使用子类来决定一个成员变量应该是哪个具体的类的实例。
关于 Javascript 使用工厂模式的优点,根据网上的总结,大概有以下几点:
a. 消除对象之间的耦合(这个不太明白,消除谁与谁的耦合?);
b. 在进行批次相关设置时,把所有实例化的代码都集中在一个位置,有助于创建模块化的代码,减少代码量;
c. 用于许多小型对象组成一个大对象。
缺点:
a. 没有解决对象识别的问题。
.....这个模式先留着吧,没太搞透彻优势在哪。以后领悟了的话再来补充。引用《Javascript 设计模式与开发实践》P5中的一段话:
“而在 Javascript 这种类型模糊的语言中,对象多态性是天生的,一个变量既可以指向一个类,又可以随时指向另外一个类。Javascript 不存在类型耦合问题,自然也没有必要刻意把对象“延迟”到子类创建,也就是说,Javascript 实际上是不需要工厂方法模式的。 模式的存在首先是能为我们解决什么问题,这种牵强的模拟只会让人觉得设计模式既难懂又没什么用。”
2. 构造函数模式
关于封装: 在 Javascript 中,可以将一些属性和方法封装到构造函数内;
关于多态:在 Javascript 中不存在重载的概念,但可以通过参数个数和类型判断来模拟重载,但是 Javascript 中多态是与生俱来的。一个最简单的栗子:
同一个构造函数实例化得到的两个对象实例,可以给它们传入不同的参数,这两个对象实例是不同的。这就是面向对象编程的多态。
利用构造函数模式,可以创建多个实例,这些实例都被标定为了特定的类型。构造函数模式相比于工厂模式更为简单且易于实现,但也存在缺点:
function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.arr = []; this.sayName = function(){ alert(this.name); }; } var person1 = new Person("Nicholas",29,"Software Engineer"); var person2 = new Person("Greg",27,"Doctor"); console.log(person1.sayName==person2.sayName);//false person1.arr.push(1); console.log(person1.arr);//1 person2.arr.push(2); console.log(person2.arr);//2
利用构造函数每实例化一个实例对象,构造函数内的方法都要在实例上重新创建一次。通过 person1.sayName==person2.sayName 返回的返回结果可以看到两个实例引用的方法是不同的。
注意:这一点也适用于数组。
可以通过将构造函数的方法放在外部,使得对象实例每次都引用同一个方法。
function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.sayName = sayName; } function sayName(){ alert(this.name); } var person1 = new Person("Nicholas",29,"Software Engineer"); var person2 = new Person("Greg",27,"Doctor"); console.log(person1.sayName==person2.sayName);//true
通过将构造函数的方法提到外部作为:特权方法。可以使实例获得相同的方法引用。
但是这种方法也存在一些问题:将方法提到外部作为特权方法使得封装性变差。如果构造函数内部有很多个方法,这样做后果更为明显。
另外再看一个问题:如果构造函数内有多个固定属性,并且存在多个方法。实例化的时候要为每个对象都复制相同的固定属性,如果将方法放在构造函数内部,这些方法也要复制,即使都作为特权函数,也存在弊端。
总结,构造函数模式存在两点不足:1. 浪费内存;2. 可能会使封装性变差。
3.原型模式
原型模式的出现,解决了构造函数实例化过程中对固定属性个方法重复深复制的问题。
对于相同的属性和方法,只要在构造函数的原型对象上申明一次,构造函数的实例就可以共享同一个属性和方法。
function Person(){ } Person.prototype.name = "Nicholas"; Person.prototype.sayName = function(){ console.log(this.name); } var person1 = new Person(); var person2 = new Person(); console.log(person1.sayName===person2.sayName);//true console.log(person1.name===person2.name);//true
4.组合使用构造函数模式和原型模式(混合模式)
但是可以看到,虽然原型模式能够解决固定属性和方法多次复制的问题,如果属性要根据不同实例对象改变,这时候最好还是把会随实例对象改变的属性放置在构造函数内部,这又用到了构造函数模式。将构造函数模式和原型模式相结合,就能利用这两个模式的优势。
function Person(name,age){ //会随实例对象改变的属性 this.name = name; this.age = age; } //不变的属性或者是方法 Person.prototype.job = "Soft Engineer"; Person.prototype.sayName = function(){ console.log(this.name); } var person1 = new Person("Nicholas",28); var person2 = new Person("Shelby",25);