6.2.3 原型模式
1、理解原型对象
2、原型与in操作符
有两种方式使用in操作符:
单独使用:in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例中还是原型中
在for-in循环中使用:在使用for-in循环时返回的是所有能够通过对象访问的、可枚举的属性。其中既包括存在于实例中的属性,也包括存在于原型中的属性。屏蔽了原型中不可枚举属性的实例也会在for-in循环中返回。
var o ={
toString:function(){
return "My Object";
}
};
for(var prop in o){
if(prop =="toString"){
alert("Found toString");//在IE中不会显示
}
}
//在IE8及更早的版本中会出现一个bug,即屏蔽不可枚举属性的实例属性不会出现在for-in循环中
要取得对象上所有可枚举的实例属性,可以使用ECMAScript5的object.keys()方法。这个方法接受一个对象作为参数,返回一个包含所有可枚举属性的 字符串数组。例:
function Person(){
}
Person.prototype.name="kathy";
Person.prototype.age=18;
Person.prototype.job="student";
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="stephen";
p1.age=19;
var p1keys=Object.keys(p1);
alert(p1keys);//name,age
3、更简单的原型语法
为了减少不必要的输入,也为了从视觉上更好的封装原型的功能,常见的做法是用一个包含所有属性和方法的对象字面量来重写整个原型对象。例:
function Person(){
}
Person.prototype={//设置为等于一个以对象字面量形式创建的新对象
name:"kahty",
age:18,
job:"student",
sayName:function(){
alert(this.name);
}
};
var friend = new Person();
alert(friend instanceof Object);//true
alert(friend instanceof Person);//true
alert(friend.constructor == Person);//false
//constructor 属性不再指向Person
alert(friend.constructor == Object);//true
前面介绍过,每创建一个函数,就会同事创建它的prototype对象,这个对象也会自动获得constructor属性。但是在这里使用的语法,本质上完全重写了默认的prototype对象,因此constructor 属性也变成了新对象的constructor 属性,不再指向Person函数。
如果constructor 的值真的很重要可以这样将它设置回适当的值:
function Person(){
}
Person.prototype={
constructor :Person,//通过这样的设置确保通过该属性能够访问到适当的 值。
name:"kahty",
age:18,
job:"student",
sayName:function(){
alert(this.name);
}
};
4、原型的动态性
在原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何修改都能够立刻从实力上反映出来。例:
var friend= new Person();
Person.prototype.sayHi=function(){
alert("hi");
};
friend.sayHi();//Hi
//首先会在实例中搜索名为sayHi的属性,没有找到会继续搜索原型。
因为实例与原型之间的连接只不过是一个指针,而非一个副本。因此可以在原型中找到新的sayHi属性并返回保存在那里的函数。
function Person(){
}
var friend = new Person();
Person.prototype={
constructor: Person,
name:"kathy",
age:18,
job:"student",
sayName : function () {
alert (this .name);
}
};
friend.sayName();//error
//在这个例子中,首先创建了Person的一个实例,然后又重写了其原型对象。
//在调用friend.sayName()时发生了错误。因为friend指向的 原型中不包含以该名字命名的属性。
image.png
重写原型对象会切断现有原型与之前已经存在的对象实例之间的联系;他们引用的仍然是最初的原型。
5、原生对象的原型
所有原生引用类型(Object、Array、Srting等等)都在其构造函数的原型上定义了方法。
例如:
Array.prototype中可以找到sort()方法
String.prototype中可以找到substring()方法
alert(typeof Array.prototype.sort);//function
alert(typeof String.prototype.substring);//function
通过原生对象的原型,不仅可以取得所有默认方法的引用,而且也可以定义新方法。可以像修改自定义对象的原型一样修改原生对象的原型,因此可以随时添加方法。
下面的代码给基本包装类型String添加了一个名为startsWith ()的方法。
String.prototype.startsWith = function(text){
return this.indexOf(text)==0;
};
var msg = "Hello world";
alert(msg.startsWith ("Hello"));
//这里新定义的startsWith ()方法会在传入的文本位于一个字符串开始时返回true。
6、原型对象的问题
原型对象会省略为构造函数传递初始化参数这一环节,结果所有势力在默认情况下都将取得相同的 属性值。
最大的问题就是由其共享的本质所导致的。
function Person(){
}
Person.prototype={
constructor: Person,
name:"kathy",
age:18,
job:"student",
friends:["payne","sweet"],
sayName : function () {
alert (this .name);
}
};
var person1=new Person();
var person2=new Person();
person1.friends.push("Van");
alert(person1.friends);//payne,sweet,Van
alert(person2.friends);//payne,sweet,Van
alert(person1.friends===person2.friends);//true
上面这个例子中。创建了Person的两个实例,接着修改了person1.friends引用的数组,添加了一个字符串。
犹豫friends数组存在于Person.prototype中而非person1中,所以同样的修改也会反映在person2.friends中。
所以这样的共享属性不建议使用。
网友评论