1.工厂模式
用函数来封装以特定接口创建对象的细节
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");
工厂模式解决了创建多个相似对象的问题,但是没有解决对象识别的问题(即怎样知道一个对象的类型)。
2.构造函数模式
按照惯例,构造函数以大写字母开头,而非构造函数应该以一个小写字母开头。
可以使用构造函数将前面的例子重写如下:
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的一个不同实例,这两个对象都有一个constructor属性,该属性指向Person。
alter(person1.constructor == Person); //true
alter(person2.constructor == Person); //true
对象的constructor属性最初是用来表示对象类型的。但是,提到检测对象类型,还是instanceof操作符要更可靠一些。
将构造函数当做函数
任何函数只要通过new操作符来调用,那它就可以作为构造函数;而任何函数如果不通过new操作符来调用,那它跟普通函数也不会有什么两样。
构造函数的问题
ECMAScript中函数是对象,使用构造函数的主要问题是每个方法都要在每个实例上重新创建一遍。
3.原型模式
创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。因此不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中。
function Person(){}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
person1.sayName(); //"Nicholas"
var person2 = new Person();
person2.sayName(); //"Nicholas"
alert(person1.sayName == person2.sayName); //true
3.1理解原型对象
image.png无论何时只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性是一个指向prototype所在函数的指针。
例如,Person.prototype.constructor指向Person。通过这个构造函数,可以继续为远行对象添加其他属性和方法。
常用的几个函数
- isPrototypeOf() :用来确定某对象是否为某实例的原型对象
alert(Person.prototype.isPrototypeOf(person1)); //true
- Object.getPrototypeOf() : 用此函数可以方便的获取一个对象的原型
alert(Object.getPrototypeOf(person1) == Person.prototype); //true
alert(Object.getPrototypeOf(person1) .name); //"Nicholas"
- hasOwnProperty() :可以检测一个属性是存在于实例中,还是存在于原型中。这个方法只在给定属性存在于对象实例中时,才会返回true;对于来自原型的属性返回false。
3.2 原型与in操作符
- in操作符单独使用
单独使用时,in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例还是原型中。
alert(person1.hasOwnProperty("name")); //false
alert("name" in person1); //true 说明来自原型
通过同时使用hasOwnProperty()方法和in操作符,就可以确定属性是否来自原型。可以定义如下函数:
function hasPrototypeProperty(object, name){
return !object.hasOwnProperty(name) && (name in object);
}
- 在for-in循环中使用
在使用for-in循环时,返回的是所有能够通过对象访问的、可枚举的属性,其中既包括存在于实例中的属性,也包括存在于原型中的属性。屏蔽了原型中不可枚举属性(即将[[Enumerable]]标记为false的属性)的实例属性也会在for-in循环中返回。
两个常用函数 - Object.keys() :取得对象上所有可枚举的实例属性。接受一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。若通过原型调用则返回的是原型中的属性,通过实例调用就只返回实例属性。
- Object.getOwnPropertyNames():得到所有实例属性,无论是否可以枚举。
3.3 更简单的原型语法
用一个包含所有属性和方法的对象字面量来重写整个原型对象
function Person(){}
Person.prototype = {
constructor : Person;
//这里本质上重写了默认的prototype对象,因此constructor属性指向Object构造函数,不再指向Person函数。因此需要设置一下,保证通过该属性能够访问到适当的值。
name : "Nicholas";
age : 29;
job : "Software Engineer";
sayName : function(){
alter(this.name);
}
}
3.4 原型的动态性
对原型对象所做的任何修改都能够立即从实例上反映出来,即使是先创建了实例后修改原型也是如此。
但如果重写了原型对象,之前创建的实例就无法反映修改变化,因为把原型修改为另一个对象就等于切断了构造函数与最初原型之间的联系。
3.5 原型对象的问题
原型中的所有属性都是被许多实例共享的,这时候引用类型值的属性就会出现问题。
=》组合使用构造函数模式和原型模式是创建自定义类型最常见的方式。
构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。重写前面的例子
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["Shelby", "Court"];
}
Person.prototype{
constructor : Person;
sayName : function(){
alert(this.name);
}
}
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
动态原型模式
前面的方法中构造函数和原型是相互独立的,这让其他OO语言经验的开发人员非常困惑。动态原型模式将所有信息都封装在构造函数中,而通过在构造函数中初始化原型(仅在必要的情况下),又保持了同时使用构造函数和原型的优点。换句话说,可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型。
function Person(name, age, job){
//属性
this.name = name;
this.age = age;
this.job = job;
this.friends = ["Shelby", "Court"];
//方法
if(typeof this.sayName != "function"){
Person.prototype.sayName = function(){
alert(this.name);
};
}
}
寄生构造函数模式
通常在前面几种模式都不适用的情况下,可以使用寄生构造函数模式。这种模式的基本思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后返回新创建的对象。除了使用new操作符并把使用的包装函数叫做构造函数以外,这个模式跟工厂模式其实一模一样。
不能依赖instanceof操作符来确定对象类型
稳妥构造函数模式
稳妥构造函数遵循与寄生构造函数类似的模式,但是有两点不同:一是新创建对象的实例方法不引用this;二是不使用new操作符调用构造函数。稳妥对象适合在一些安全的环境中,或者在防止数据被其他应用程序改动时使用。
不能依赖instanceof操作符来确定对象类型
网友评论