1) 构造函数模式
es中的构造函数可以用来创建特定类型的对象,像Object和Array这样的原生构造函数,在运行时会自动出现在执行环境中,此外,也可以创建自定义的构造函数,从而定义自定义对象类型的属性和方法,例如:
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 preson2=new Person(“Greg”,27,”Doctor”);
要创建Person的新实例,必须使用new操作符,以这种方式调用构造函数实际上火经历一下4个步骤:
- l 创建一个新对象;
- l 将构造函数的作用域赋给新的对象(因此this就指向这个新对象);
- l 执行构造函数中的代码(为这个新对象添加属性);
- l 返回新对象.
对象的constructor属性最初是用来标识对象类型的,但是,提到检测对象类型,还是instanceof操作符要更可靠一些,我们在这个例子中创建的所有对象既是Object的实例,同时也是Person的实例,这一点通过instanceof操作符可以得到验证.
alert(person1 instanceof Object);//true
alert(person1 instanceif Person);//true
alert(person2 instanceof Object);//true
alert(person2 instanceof Person);//true
构造函数模式虽然好用,但也并非没有确定,使用构造函数的主要问题,就是每个方法都要在每个实例上重新创建一遍,而sayName()的方法去死不同的Function的实例.
2) 原型模式
我们创建的每一个函数都有一个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();
person1.sayName();//”Nicholas”
Alert(person1.sayName==person2.sayName)://true
- l 理解原型对象
无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象,在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针,就上面例子来说,Preson.prototype.constructor指向Person,而通过这个构造函数,我们还可以继续为原型对象添加其他属性和方法.
创建了自定义的构造函数之后,其原型对象默认只会取得constructor属性,至于其他方法,则都是从Object继承而来的,当调用构造函数创建一个新实例后,该实例内部将包含一个指针(内部属性),指向构造函数的原型对象,ECMA-262第5版中管这个指针叫[[prototype]],虽然在脚本中没有标准的方式访问[[prototype]],但ff,safari和chrome在每个对象上都支持一个属性_proto_;而在其他实现中,这个属性对脚本则是完全不可见的。
虽然在所以实现中都无法范围到[[prototype]],但可以通过isPrototypeOf()方法来确定对象之间是否存在这种关系,从本质上讲,如果[[prototype]]指向调用isPrototypeOf()方法的对象(Person.prototype),那么这个方法就返回true,如下:
alert(Person.prototype.isPrototypeOf(person1));//true
alert(Person.prototype.isPrototypeOf(person2));//true
这里,我们用原型对象的isPrototypeOf()方法测试了person1和person2,因为它们内部都有个指向Person.prototype的指针,因此都返回了true;
es5增加了一个新方法叫 Object.getPrototypeOf(),在所有支持的实现中,这个方法返回[[prototype]]的值,例如:
alert(Object.getPrototypeOf(person1)==Person.prototype);//true
alert(Object.getPrototypeOf(person2).name);//”Nicholas”
每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性,搜索首先从对象实例本身开始,如果在实例中找到了具有给定名字的属性,则返回该属性的值;如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性,如果在原型对象中找到了这个属性,则返回该属性的值。
使用hasOwnPrototype()方法可以检测一个属性是存在于实例中,还是存在于原型中,这个方法值在给杜属性存在于对象实例中时才返回true,看下面例子:
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();
var person2=new Person();
alert(person1.hasOwnProperty(“name”))//false
person1.name=”Greg”;
alert(person1.name)//”Greg”
alert(person1.hasOwnProperty(“name”))//true
alert(person2.name)//”Nicholas”
alert(person2.hasOwnproperty(“name”));//false
delete person1.name
alert(person1.name);//”Nicholas”
alert(person1.hasOwnProperty(“name”));//false
IE早期版本的实现中存在一个bug,即屏蔽不可枚举属性的实例属性不会出现在for-in循环中,例如:
var o={
toString:function(){
Return “My object”
}
};
for (var prop in o){
If(prop==”toString”){
alert(“Found toString”);
}
}
当以上代码运行时,应该会显示一个警告框,表明找到了toString()方法,这里的对象o定义了一个名为toString的方法,该方法屏蔽了原型中的toString方法,在IE中,由于其实现认为原型的toString方法被打伤了[[Enumerable]]标记就应该跳过该属性,结果我们不会看到警告框.该bug会影响默认不可枚举的所有属性和方法。
要取得对象上所有可枚举的实例属性,可以使用es5的Object.keys方法,这个方法接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组,例如:
function Person(){
}
Person.prototype.name=”Nicholas”;
Person.prototype.age=29;
Person.prototype.job=”Software Engineer”;
Person.prototype.sayName=function(){
alert(this.name);
};
var keys=Object.keys(Person.prototype);
alert(keys);//”name,age,job,sayName”
var p1=new Person();
p1.name=”Rob”;
p1.age=31;
var p1keys=Object.keys(p1);
alert(p1keys);//”name,age”
如果你想要得到所有实例属性,无论它是否可枚举,都可以使用Object.getOwnPropertyNames()方法
var keys=Object.getOwnPropertyNames(Person.prototype);
alert(keys);//”constructor,name,age,job,sayName”