1 <script type="text/javascript">
2 //1、理解原型对象
3 //2、原型与in操作符
4 //3、更简单的原型语法
5 //4、原型的动态性
6 //5、原生对象原型
7 //6、原型对象的问题
8
9 //1、无论什么时候,只要创建了一个函数,就会根据一组特定的规则,为该函数创建一个prototype属性,该属性指向函数的原型对象
10 //在默认情况下,所有的原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针
11 //如
12 function Person(){
13
14 }
15 //Person.prototype.constructor 指向Person
16 //创建了自定义的构造函数之后,其原型对象默认只会取得constructor属性,至于其他方法则都是从Object继承而来
17 //当调用函数的创建一个新实例之后,该实例的内部包含一个指针(内部属性)指向构造函数的原型对象
18 //在Firefox、safari、chrome在每个对象上都支持一个属性_proto_访问
19 var p1=new Person();
20 alert(Person.prototype.isPrototypeOf(p1))
21
22 alert(Object.getPrototypeOf(p1)==Person.prototype)
23
24 //虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。如果我们在实例中添加了一个属性
25 //而该属性的名称与原型的中的实例同名,那我们就在实例中创建该属性,该属性将会屏蔽原型中的那个属性。eg:
26 function Person() {
27 }
28 Person.prototype.name="amber";
29 Person.prototype.age=23;
30 Person.prototype.job="software engineer";
31 Person.prototype.sayName=function(){
32 alert(this.name)
33 }
34
35 var person1=new Person();
36 var person2=new Person();
37 person1.name="amber.Xu";
38 alert(person1.name);//amber.xu --来自实例
39 alert(person2.name);//amber --来自原型
40
41 delete person1.name;
42 alert(person1.name);//amber --来自原型
43
44 //使用hasOwnProperty()方法可以检测一个属性是存在于实例中还是存在于原型中,这个方法(从Object继承而来)
45 //只在给定属性存在于对象实例中时,才会返回true
46 function Person() {
47 }
48 Person.prototype.name="amber";
49 Person.prototype.age=23;
50 Person.prototype.job="software engineer";
51 Person.prototype.sayName=function(){
52 alert(this.name)
53 }
54 var person1=new Person();
55 var person2=new Person();
56
57 alert(person1.hasOwnProperty("name"));//false 来自实例
58
59 alert(person2.hasOwnProperty("name"));//false 来自实例
60
61 person1.name="amber.xu";
62 alert(person1.name);
63 alert(person1.hasOwnProperty("name"));//true 来自实例
64
65 delete person1.name;
66 alert(person1.name);
67 alert(person1.hasOwnProperty("name"));//false 来自原型
68
69
70
71 //2、原型与in操作符
72 //in 有两种使用方式,一个是的单独使用和在for-in 中使用。在单独使用时,in操作符会在对象能够访问给定属性时返回true
73 //无论该属性时来自原型还是实例
74 function Person() {
75 }
76 Person.prototype.name="amber";
77 Person.prototype.age=23;
78 Person.prototype.job="software engineer";
79 Person.prototype.sayName=function(){
80 alert(this.name)
81 }
82 var person1=new Person();
83 var person2=new Person();
84 alert("name" in person1);//true 来自原型
85 alert("name" in person2);//true 来自原型
86 alert("height" in person1);//false
87
88
89 //这样就可以封装一个函数(给定属性是否是来给定对象的原型)
90 function hasPrototypeProperty(object,name){
91 return !object.hasOwnProperty(name) && (name in object);
92 }
93 alert("----------------------------------");
94 alert(hasPrototypeProperty(person1,"name"));//true
95
96 person1.name="张三";
97 alert(hasPrototypeProperty(person1,"name"));//false
98
99
100 //使用for-in 返回的是所有能够通过对象访问、可枚举的属性,其中既包含原型属性也包含实例属性。
101 //屏蔽了原型中不可枚举属性(将Enumerable标记为false的属性)的实例属性也会在for-in中返回
102 //ie早期版本总中有一个bug:屏蔽了原型中不可枚举属性的实例属性也不会在for-in中返回
103 //eg:
104 var o={
105 toString:function(){
106 return "my object";
107 }
108 };
109
110 for(var prop in o){
111 if(prop=="toString"){
112 alert("找到了");//在ie早期版本中不会显示
113 }
114 }
115
116 //要取得对象上所有可枚举的属性,可以使用ECMAScript5的Object.keys()方法。接受一个对象作为参数,
117 //包含所有可枚举属性的字符串数组
118 function Person() {
119 }
120 Person.prototype.name="amber";
121 Person.prototype.age=23;
122 Person.prototype.job="software engineer";
123 Person.prototype.sayName=function(){
124 alert(this.name)
125 }
126 var person1=new Person();
127 var person2=new Person();
128 var keys=Object.keys(Person.prototype);
129 alert(keys)
130
131 person1.name="amber.Xu";
132 person1.age=23;
133 var keys=Object.keys(person1);
134 alert(keys)
135
136 alert("-----------------------------------------")
137 //如果想要得到所有的实例属性不管他是否可以枚举,都可以使用
138 alert(Object.getOwnPropertyNames(person1));
139 alert(Object.getOwnPropertyNames(Person.prototype));
140
141 alert("更简单的原型语法-----------------------------------------")
142 //3、更简单的原型语法
143 function Person() {
144
145 }
146
147 Person.prototype={
148 name:"AMBER",
149 age:23,
150 job:"software",
151 sayName:function(){
152 alert(this.name)
153 }
154 }
155
156 //这样写之后constructor属性不再指向Person函数,而是指向Object构造函数。
157 //尽管通过instanceof操作符还能返回正确的结果,但是通过constructor已经无法确定对象的类型了,eg:
158 var friend=new Person();
159 alert(friend instanceof Person)//true
160 alert(friend instanceof Object)//true
161 alert(friend.constructor==Person);//false
162 alert(friend.constructor==Object);//true
163 //如果constructor对你真的很重要,可以向下面一样设置成适当的值
164
165 function Person() {
166
167 }
168
169 Person.prototype={
170 constructor:Person,
171 name:"AMBER",
172 age:23,
173 job:"software",
174 sayName:function(){
175 alert(this.name)
176 }
177 }
178 var friend=new Person();
179 alert("手动设置constructor-----------------------------------------")
180 alert(friend.constructor==Person);//true
181
182 //这种手动的添加了constructor会使constructor变成可枚举的元(原生的constructor属性时不可枚举的)。
183 //这种情况下就可以使用
184 Object.defineProperty(Person.prototype,"constructor",{
185 enumerable:false,
186 value:Person
187 });
188
189
190 //原型的动态性
191 var friend=new Person();
192 Person.prototype.sayHi=function(){
193 alert("Hi");
194 }
195
196 friend.sayHi();//Hi (正常执行)
197 //因为实例和原型之间是松散的连接关系,实例与原型之间的连接只不过是一个指针,而非副本
198 //当我们调用sayHi()方法时,首先会在实例中搜索名为sayHi的方法,在没找到的情况下会搜索原型。
199
200 //但是,如果是重写整个原型对象,那么情况就不一样了。
201 //我们知道,调用构造函数时会为实例添加一个指向最初原型的Prototype指针,而把原型修改为另一个对象就等于切断了构造函数与最初原型之间的联系。
202 //请记住:实例中的指针仅指向原型,而不指向构造函数。eg:
203 function A(){}
204 var a1=new A();
205 A.prototype={
206 constructor:A,
207 name:"AMBER",
208 age:23,
209 job:"software",
210 sayName:function(){
211 alert(this.name)
212 }
213 }
214 alert("ERROR-------------------------------------");
215 alert(a1.sayName());
216 //我们创建了一个A的实例,然后又重写了其原型对象,然后在调用a1.sayName()发生了错误,因为a指向的原型中不包含以该名字命名的属性/方法
217
218 //原生对象的原型
219 //原型模式的重要性不仅体现在创建自定义类型方面。就连所有的原生的引用类型,都是采用这种模式创建的。所有的原生引用类型
220 //都在其构造函数的原型上定义的方法 eg:
221 alert(typeof Array.prototype.sort);//function
222 alert(typeof String.prototype.substring);//function
223 //不仅可以在原生对象的原型取得虽有默认方法的引用,而且可以定义新的方法
224 //为String类型添加一个startsWith()的方法
225 String.prototype.startsWith=function(text){
226 return this.indexOf(text) == 0;
227 };
228 var msg="Hello";
229 alert(msg.startsWith("H"));
230
231 //我们并不建议这样做。
232
233 alert("原型对象的问题");
234 //6、原型对象的问题 实例
235 function Ques() {
236 }
237
238 Ques.prototype={
239 constructor:Ques,
240 name:"amber",
241 age:23,
242 job:"IT",
243 friends:["张三","李四"],//引用类型
244 sayName:function(){
245 alert(this.name)
246 }
247 };
248
249 var q1=new Ques();
250 var q2=new Ques();
251 q1.friends.push("王五");
252 alert(q1.friends);//
253 alert(q2.friends);//
254 alert(q1.friends===q2.friends);
255 //相信大家已经看到了问题,当我创建了两个实例q1、q2,当我为q1的“朋友”添加了“王五”之后,q2的”朋友“也有了三个张三、李四、王五
256 //那是因为数组存在于Ques.prototype上,而非q1上。所以出现了如上结果。
257
258 //而正是这个问题,我们很少看到有人单独使用原型模式的原因所在。
259 </script>