Null's Blog

Javascript中的对象2

原型模式1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Person(name,age,job){
}
Person.prototype.name="nUll";
Person.prototype.age=25;
Person.prototype.job="software";
Person.prototype.sayName=function(){
alert(this.name);
};
var person1=new Person();
person1.sayName(); //nUll
var person2=new Person();
person2.sayName(); //nUll
alert(person1.sayName==person2.sayName); //true

原型中的所有属性与方法是所有实例所共享的.
person1的内部属性prototype 指向 Person.prototype
Person.prototype指向原型对象
而原型对象中的构造函数属性constructor 指向Person
注意,person1与person2与它们的构造函数没有直接关系,虽然这两个实例都不包含属性和方法,但是却可以调用
person1.sayName();

这里简述一下查找sayName的查找过程,类似于作用域链的查找过程,由下至上,即实例-原型
person1与person2与它们的构造函数没有直接关系,那如何确定他们之间的间接关系呢?

1
2
alert(Person.prototype.isPrototypeOf(person1));//true
alert(Person.prototype.isPrototypeOf(person2));//true

person1的内部属性prototype,在脚本中是无法直接访问的.但是可以通过其他方法访问,例如在ECMAScript5 中提供了Object.getPrototypeOf()方法,至于javascript与ECMAScript5 的关系,可以google一下.

1
2
alert(Object.getPrototypeOf(person1)==Person.prototype);//true
alert(Object.getPrototypeOf(person2).name);//nUll

Object.getPrototypeOf()方法返回的对象,就是这个实例的原型
如果给实例添加一个属性,这个属性与原型中的某个属性名称一样,那么该属性就会屏蔽原型中的属性.

1
2
3
4
5
6
7
var person1=new Person();
var person2=new Person();
person1.name="mywei";
alert(person1.name); //mywei 来至实例
alert(person2.name); //nUll 来至原型
delete person1.name; //删除实例中的 name属性
alert(person1.name); //nUll 又来至原型了

如何判断实例的属性值来自实例还是来至原型?

1
2
3
4
5
6
7
8
9
10
var person1=new Person();
var person2=new Person();
alert(person1.hasOwnProperty("name")); //false
person1.name="mywei";
alert(person1.name); //mywei 来至实例
alert(person2.name); //nUll 来至原型
alert(person1.hasOwnProperty("name")); //true
delete person1.name; //删除实例中的 name属性
alert(person1.name); //nUll 又来至原型了
alert(person1.hasOwnProperty("name")); //false

实用代码:

1
2
3
4
5
6
7
function hasPrototypeProperty(object,name){
return !object.hasOwnProperty(name)&&(name in object);
}
var person1=new Person();
alert(hasPrototypeProperty(person,"name")); //false 来自原型
person1.name="mywei";
alert(hasPrototypeProperty(person,"name")); //true 来自实例

以上 name in object中的in操作符是判断 name的值是否存在于原型或实例中.其实,in操作符只能枚举必须是可枚举的([Enumerable]=true),属性有几种特性?google一下吧.
另一种原型模式创建对象的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
function Person(){
}
Person.prototype.name="nUll";
Person.prototype.age=25;
Person.prototype.job="software";
Person.prototype.sayName=function(){
alert(this.name);
};
var person1=new Person();
alert(person1 instanceof Object); //true
alert(person1 instanceof Person);//true
alert(person1.constructor==Person);//true
alert(person1.constructor==Object);//true
//--------------------------邪恶的分割线------------------------------------------//
function Person(){
}
Person.prototype={
name:"nUll",
age:25,
job="software",
sayName=function(){
alert(this.name);
}
}
var person2=new Person();
alert(person2 instanceof Object); //true
alert(person2 instanceof Person);//true
alert(person2.constructor==Person);//false 注意,这里变成了false原因在于这里的"字面量写法",覆盖了原型里的所有属性,包括constructor,而默认的constructor 是 Object
alert(person2.constructor==Object);//true
//怎么破? 写回去!
function Person(){
}
Person.prototype={
constructor:"Person",
name:"nUll",
age:25,
job="software",
sayName=function(){
alert(this.name);
}
};
//可惜的是,这样写回去会改变constructor属性的特性[Enumerable],默认情况下,constructor属性是不可枚举的.更好的写法是这样,当然,这是在建立在如何设定特性值的情况下,刚才说让你google的...:
function Person(){
}
Person.prototype={
constructor:"Person",
name:"nUll",
age:25,
job="software",
sayName=function(){
alert(this.name);
}
};
Object.defineProperty(Person.prototype,"constructor",{
enumerable:false,
value:Person
});

原型的动态性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Person(){}
var person1=new Person();
Person.prototype.sayHi=function(){
alert("hi");
};
person1.sayHi(); //hi 没有问题!原型中新增了sayHi方法,先找实例,没找到就找原型.
function Person(){}
var person1=new Person();
Person.prototype(
sayHi:function(){
alert("hi");
}
};
person1.sayHi(); //error 刚说过了,尽管上面为原型添加属性和方法可以在所有对象的实例中反映出来,但这种"字面量"写法重写了整个原型,情况就不一样了.这种重写已经改变原有对象与构造函数之间的联系,它是一个新的原型(虽然旧的原型没有显式定义,但它确实存在?)

如何为通过原型模式为原生对象添加方法:

1
2
3
4
5
String.prototype.startsWith=function(text){
return this.indexOf(text)==0;
}
var msg="Hello world";
alert(msg.startsWith("Hello")); //true 说的好!但这毫无意义(这里仅是提供扩展的示例,并不是说扩展的startsWith方法多么的精辟!)

原型对象的缺点:

1
2
3
4
5
6
7
8
9
10
11
12
13
function Person(){}
Person.prototype={
name:"nUll",
friends:["gay1","gay2"],
sayName:function(){
alert(this.name);
}
};
var person1=new Person();
var person2=new Person();
person1.friends.push("gay3");
alert(person2.friends); //gay1,gay2,gay3
//WTF? person2 的friends 怎么会有gay3? 好吧,如果这就是你想要的结果,就当书上什么也没说-_-