注意,以这种方式重设 constructor 属性会导致他的 [[Enumerable]] 特性被设置为 true 。默认情况下,原生的 constructor 属性是不可枚举的,因此,如果你使用兼容 ECMAScript 5 的 JavaScript 引擎,你可以试试 Object.defineProperty() 方法:
function Person(){ } Person.prototype = { name : "Nicholas", age : 29, job : "Software Engineer", sayName : function () { alert(this.name); } }; //重设构造函数,只适用于 ECMAScript 5 兼容的浏览器 Object.defineProperty( Person.prototype, "constructor", { enumerable: false, value: Person });
注意,重写原型对象会切断新原型与已经存在的对象实例之间的联系;它们引用的仍然是最初的原型。
优点:对自定义类型的方法解决了函数复用的问题。
缺点:
① 不能为构造函数传递初始化参数;
② 原型模式中实现了对于包含引用类型值的属性的共享,这就意味着一个实例中修改了该引用类型值,所有实例的该属性都会被修改!!!
4. 组合使用构造函数模式和原型模式
在组合使用构造函数模式和原型模式中,构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性,而且还支持向构造函数传递参数。如以下示例代码所示:
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.friends = ["Shelby", "Court"]; } Person.prototype = { sayName : function(){ alert(this.name); } } Object.defineProperty( Person.prototype, "constructor", { enumerable: false, value: Person ); var person1 = new Person("Nicholas", 29, "Software Engineer"); var person2 = new Person("Greg", 27, "Doctor"); person1.friends.push("Van"); alert(person1.friends); //"Shelby,Count,Van" alert(person2.friends); //"Shelby,Count" alert(person1.friends === person2.friends); //false alert(person1.sayName === person2.sayName); //true
优点:能为构造函数传递初始化参数;该复用复用,不该复用的没复用。
缺点:封装性不好,构造函数和原型分别独立于父级作用域进行申明。
5. 动态原型模式(推荐)
该模式把所有信息都封装在构造函数中,通过构造函数来实现初始化原型 (仅在必要的情况下),又保持了同时使用构造函数和原型的优点。请看以下示例代码:
function Person(name, age, job){ //属性 this.name = name; this.age = age; this.job = job; //方法 if (typeof this.sayAge != "function"){ // 此处应该永远去判断新添加的属性和方法 Person.prototype.sayName = function(){ alert(this.name); }; Person.prototype.sayAge = function(){ alert(this.age); }; } } var friend = new Person("Nicholas", 29, "Software Engineer"); friend.sayName();
if 语句检查的可以是初始化之后应该存在的任何属性或方法——不必用一大堆 if 语句检查每个属性和每个方法;只要检查其中一个即可。
注意,使用动态原型模式时,不能使用对象字面量重写原型。前面已经解释过了,如果已经创建的实例的情况下重写原型,那么就会切断新原型与现有实例之间的联系。
优点:封装性非常好;还可使用 instanceOf 操作符确定它的类型。
缺点:无。
6. 寄生构造函数模式
除了使用 new 操作符并把使用的包装函数叫做构造函数之外,这个模式跟工厂模式其实是一模一样的。请看以下代码:
function Person(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 friend = new Person("Nicholas", 29, "Software Engineer"); friend.sayName(); //"Nicholas"
在使用 new 操作符下,构造函数在不返回值的情况下,默认会返回新对象实例。而通过在构造函数的末尾添加一个 return 语句,可以重写调用构造函数时返回的值。
缺点:没有解决对象识别的问题(即不知道这个对象是什么类型),不能依赖 instanceOf 操作符来确定对象类型;对于对象的方法没有做到复用。
7. 稳妥构造函数模式
先来了解下稳妥对象:指的是没有公共属性,而且其方法也不引用 this 的对象。稳妥对象最适合在一些安全的环境中 (这些环境中会禁止使用 this 和 new),或者再防止数据被其他应用程序 (如 Mashup 程序) 改动时使用。稳妥构造函数遵循与寄生构造函数类似的模式,但有两点不同:一是新创建对象的实例方法不引用 this;二是不使用 new 操作符调用构造函数。以下为示例代码:
function Person(name, age, job){ var o = new Object(); //创建要返回的对象 //可以在这里定义私有变量和函数 o.sayName = function(){ //添加方法 alert(name); }; return o; //返回对象 } var friend = Person("Nicholas", 29, "Software Engineer"); friend.sayName(); //"Nicholas"
其原理就是利用闭包,保有对私有变量和私有方法的引用。