楔子
问题
在 java 中,首先定义一个 class,然后 new 出该 class 的实例,那么无论 new 多少次,新出现的类永远都具有 class 中定义的属性。
但 js 中不一样,js 中为某个对象新添加的属性,不会出现在新对象中。如下:
var o1 = {}
o1.name = 'o1.name'
var o2 = {}
alert(o2.name)
首先为 o1 创建 name 属性,但这个属性并没有被带到 o2 中,如果 o2 也想具有与 o1 一样的属性,就需要将 o1 的属性定义全部重写一遍。这就导致了一个问题 想要创建一个类似的对象,就需要写很多重复代码。
假设有对象 A,现在想创建一个跟 A 只有一个属性不同的对象 B。按上述思路,我们需要重新创建一个 B 对象,然后将 A 、B 对象共用的属性与方法赋值给 B,代码繁琐。
工厂方法模式
为解决上述问题,可以使用工厂方法模式。
function createObj(){
var d = new Object();
d.h='h';
return d;
}
通过上述方法产生的对象都会具有 h 属性。但由于工厂方法具有封装性,外界使用者根本不知道产生的对象是哪一个类的实例,也无法修改返回对象对应的类,因此,使用者可能没办法调用某些类特有的方法。
例如使用 createObj 方法产生对象后, 想调用 getFullYear() 方法是不可能的。因此 Object 对象没有 getFullYear() 方法。如果想使用 getFullYear() 就需要改成如下代码:
function creatObj2(){
var d = new Date();
d.h='h';
return d;
}
这会有几个问题:1,使用者不能修改工厂方法里的代码;2. 即使修改了,也没有办法保证工厂方法能兼容所有的类,类的个数无限,不可能为每一个类创建一个工厂方法。
构造函数
构造函数也是函数,也可以直接调用;每一个函数都可以转成构造函数,从而可以创建实例
-
与 new 关键字结合时,普通函数会转为构造函数,进而创建出一个对象,该类的类名就是构造函数的方法名
var o = function(){ this.age = arguments[0]; // this 指代的是新创建出来的对象 } var oo = new o('age'); // 与 new 关键字结合,转为构造函数,进而创建一个实例 alert(oo.age) alert(oo instanceof o) // true o('直接调用'); //直接调用时,this 指代的是 window alert(window.age+",from window") // 所以 window.age 输出的是 ‘直接调用’
-
由于 this 指代的是当前对象,所以在构造函数中的 this 指的就是新创建的对象。
function Demo(){ this.m = function(){ alert('this is m ') } } var o = new Object(); Demo.call(o) o.m()
如果不调用 Demo.call() 就没办法直接调用 o.m() ,因为 Object 类中没有方法名为 m 的方法。
调用 call 方法后,相当于在 o 对象中运行了一次 Demo 方法,而其中的 this 指的就是 o 对象,所以 o 对象中已经具有了 m 方法。 -
与工厂方法相比,构造函数有如下优点:
没有 new Object()
直接将属性赋值给 this ,而不是赋值给一个 Object 对象
不需要直接返回一个对象。
返回的对象不但 instanceof Object ,而且还 instanceof 构造函数对应的类。
构造函数实质上与工厂方法一样,只不过 new Object() 与 return 语句是在后台进行。构造函数算是一个改良后的工厂方法。
一般来说,构造函数首字母大写,而普通函数不需要。