注釋里講解的十分細致了,這里就不多廢話了,直接上代碼:
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
<script type= "text/javascript" > //ECMA-262把對象定義為:“無序屬性的 集合,其屬性可以包含基本值、對象或者函數” //理解對象,最簡單的方式就是通過創建一個Object的實例,然后為它添加屬性和方法 var person = new Object(); person.name = "Xulei" ; person.age = "23" ; person.job = "前端工程師" ; person.sayName = function () { alert( this .name); } //還可以這樣寫 var person = { name: "xulei" , age: 23, job: "前端工程" , sayName: function () { alert( this .name) } } //一、屬性類型:數據屬性和訪問其屬性 //1、數據屬性,有4個描述其行為的特性 //[Configurable]:表示能否通過delete刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把屬性修改為訪問器屬性,默認值為true //[Enumerable]:表示能否通過for-in返回屬性,默認值為true //[Writable]:表示能否修改屬性,默認值為true //[Value]:包含這個屬性的數據值。默認值為undefined var person = { name: "xulei" } //這里創建了一個person對象,value值就是“xulei” //要修改屬性的默認特性,必須使用ECMAScript5的Object.defineProperty(屬性所在的對象,屬性的名字,描述符對象) //描述符對象必須是configurable、enumerable、writable、value var peron = {} Object.defineProperty(peron, "name" , { writable: false , //屬性不能被修改 value: "徐磊-xulei" }); alert(peron.name); //徐磊-xulei peron.name = "徐磊" ; alert(peron.name); //徐磊-xulei //以上操作在非嚴格模式下賦值操作會被忽略,如果在嚴格模式下會拋出異常 //一旦把屬性定義為不可配置的就不能把它變回可配置的了。 //在多數情況下都沒有必要利用Object.defineProperty()方法提供的這些高級功能。但是對理解javascript非常有用。 //建議讀者不要在ie8上使用此方法。 //2、訪問其屬性,有4個特性 //[Configurable]:表示能否通過delete刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把屬性修改為訪問器屬性,默認值為true //[Enumerable]:表示能否通過for-in返回屬性,默認值為true //[Get]:在讀取時調用的函數 默認值undefined //[Set]:在寫入屬性時調用的函數 默認值Undefined var book={ _year:2004, edition:1 } Object.defineProperty(book, "year" ,{ get: function (){ return this ._year; }, set: function (value){ if (value>2004){ this ._year=value; this .edition +=value-2004; } } }); book.year=2005; alert(book.edition); //2 //創建對象 //1、將構造函數當做函數 function Person(name,age,job) { this .name=name; this .age=age; this .job=job; this .sayName= function (){ alert( this .name); } } //當做構造函數使用 var person= new Person( "xulei" ,23, "software" ); person.sayName(); //作為普通函數使用 Person( "xulei2" ,23, "job2" ); //添加到window中 window.sayName(); //在另一個對象的作用域中調用 var o= new Object(); Person.call(o, "xulei3" ,23, "job3" ); o.sayName(); </script> |
再來一段:
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
|
<script type= "text/javascript" > //1、理解原型對象 //2、原型與in操作符 //3、更簡單的原型語法 //4、原型的動態性 //5、原生對象原型 //6、原型對象的問題 //1、無論什么時候,只要創建了一個函數,就會根據一組特定的規則,為該函數創建一個prototype屬性,該屬性指向函數的原型對象 //在默認情況下,所有的原型對象都會自動獲得一個constructor(構造函數)屬性,這個屬性包含一個指向prototype屬性所在函數的指針 //如 function Person(){ } //Person.prototype.constructor 指向Person //創建了自定義的構造函數之后,其原型對象默認只會取得constructor屬性,至于其他方法則都是從Object繼承而來 //當調用函數的創建一個新實例之后,該實例的內部包含一個指針(內部屬性)指向構造函數的原型對象 //在Firefox、safari、chrome在每個對象上都支持一個屬性_proto_訪問 var p1= new Person(); alert(Person.prototype.isPrototypeOf(p1)) alert(Object.getPrototypeOf(p1)==Person.prototype) //雖然可以通過對象實例訪問保存在原型中的值,但卻不能通過對象實例重寫原型中的值。如果我們在實例中添加了一個屬性 //而該屬性的名稱與原型的中的實例同名,那我們就在實例中創建該屬性,該屬性將會屏蔽原型中的那個屬性。eg: function Person() { } Person.prototype.name= "amber" ; Person.prototype.age=23; Person.prototype.job= "software engineer" ; Person.prototype.sayName= function (){ alert( this .name) } var person1= new Person(); var person2= new Person(); person1.name= "amber.Xu" ; alert(person1.name); //amber.xu --來自實例 alert(person2.name); //amber --來自原型 delete person1.name; alert(person1.name); //amber --來自原型 //使用hasOwnProperty()方法可以檢測一個屬性是存在于實例中還是存在于原型中,這個方法(從Object繼承而來) //只在給定屬性存在于對象實例中時,才會返回true function Person() { } Person.prototype.name= "amber" ; Person.prototype.age=23; 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 來自實例 alert(person2.hasOwnProperty( "name" )); //false 來自實例 person1.name= "amber.xu" ; alert(person1.name); alert(person1.hasOwnProperty( "name" )); //true 來自實例 delete person1.name; alert(person1.name); alert(person1.hasOwnProperty( "name" )); //false 來自原型 //2、原型與in操作符 //in 有兩種使用方式,一個是的單獨使用和在for-in 中使用。在單獨使用時,in操作符會在對象能夠訪問給定屬性時返回true //無論該屬性時來自原型還是實例 function Person() { } Person.prototype.name= "amber" ; Person.prototype.age=23; Person.prototype.job= "software engineer" ; Person.prototype.sayName= function (){ alert( this .name) } var person1= new Person(); var person2= new Person(); alert( "name" in person1); //true 來自原型 alert( "name" in person2); //true 來自原型 alert( "height" in person1); //false //這樣就可以封裝一個函數(給定屬性是否是來給定對象的原型) function hasPrototypeProperty(object,name){ return !object.hasOwnProperty(name) && (name in object); } alert( "----------------------------------" ); alert(hasPrototypeProperty(person1, "name" )); //true person1.name= "張三" ; alert(hasPrototypeProperty(person1, "name" )); //false //使用for-in 返回的是所有能夠通過對象訪問、可枚舉的屬性,其中既包含原型屬性也包含實例屬性。 //屏蔽了原型中不可枚舉屬性(將Enumerable標記為false的屬性)的實例屬性也會在for-in中返回 //ie早期版本總中有一個bug:屏蔽了原型中不可枚舉屬性的實例屬性也不會在for-in中返回 //eg: var o={ toString: function (){ return "my object" ; } }; for ( var prop in o){ if (prop== "toString" ){ alert( "找到了" ); //在ie早期版本中不會顯示 } } //要取得對象上所有可枚舉的屬性,可以使用ECMAScript5的Object.keys()方法。接受一個對象作為參數, //包含所有可枚舉屬性的字符串數組 function Person() { } Person.prototype.name= "amber" ; Person.prototype.age=23; Person.prototype.job= "software engineer" ; Person.prototype.sayName= function (){ alert( this .name) } var person1= new Person(); var person2= new Person(); var keys=Object.keys(Person.prototype); alert(keys) person1.name= "amber.Xu" ; person1.age=23; var keys=Object.keys(person1); alert(keys) alert( "-----------------------------------------" ) //如果想要得到所有的實例屬性不管他是否可以枚舉,都可以使用 alert(Object.getOwnPropertyNames(person1)); alert(Object.getOwnPropertyNames(Person.prototype)); alert( "更簡單的原型語法-----------------------------------------" ) //3、更簡單的原型語法 function Person() { } Person.prototype={ name: "AMBER" , age:23, job: "software" , sayName: function (){ alert( this .name) } } //這樣寫之后constructor屬性不再指向Person函數,而是指向Object構造函數。 //盡管通過instanceof操作符還能返回正確的結果,但是通過constructor已經無法確定對象的類型了,eg: var friend= new Person(); alert(friend instanceof Person) //true alert(friend instanceof Object) //true alert(friend.constructor==Person); //false alert(friend.constructor==Object); //true //如果constructor對你真的很重要,可以向下面一樣設置成適當的值 function Person() { } Person.prototype={ constructor:Person, name: "AMBER" , age:23, job: "software" , sayName: function (){ alert( this .name) } } var friend= new Person(); alert( "手動設置constructor-----------------------------------------" ) alert(friend.constructor==Person); //true //這種手動的添加了constructor會使constructor變成可枚舉的元(原生的constructor屬性時不可枚舉的)。 //這種情況下就可以使用 Object.defineProperty(Person.prototype, "constructor" ,{ enumerable: false , value:Person }); //原型的動態性 var friend= new Person(); Person.prototype.sayHi= function (){ alert( "Hi" ); } friend.sayHi(); //Hi (正常執行) //因為實例和原型之間是松散的連接關系,實例與原型之間的連接只不過是一個指針,而非副本 //當我們調用sayHi()方法時,首先會在實例中搜索名為sayHi的方法,在沒找到的情況下會搜索原型。 //但是,如果是重寫整個原型對象,那么情況就不一樣了。 //我們知道,調用構造函數時會為實例添加一個指向最初原型的Prototype指針,而把原型修改為另一個對象就等于切斷了構造函數與最初原型之間的聯系。 //請記住:實例中的指針僅指向原型,而不指向構造函數。eg: function A(){} var a1= new A(); A.prototype={ constructor:A, name: "AMBER" , age:23, job: "software" , sayName: function (){ alert( this .name) } } alert( "ERROR-------------------------------------" ); alert(a1.sayName()); //我們創建了一個A的實例,然后又重寫了其原型對象,然后在調用a1.sayName()發生了錯誤,因為a指向的原型中不包含以該名字命名的屬性/方法 //原生對象的原型 //原型模式的重要性不僅體現在創建自定義類型方面。就連所有的原生的引用類型,都是采用這種模式創建的。所有的原生引用類型 //都在其構造函數的原型上定義的方法 eg: 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" ; alert(msg.startsWith( "H" )); //我們并不建議這樣做。 alert( "原型對象的問題" ); //6、原型對象的問題 實例 function Ques() { } Ques.prototype={ constructor:Ques, name: "amber" , age:23, job: "IT" , friends:[ "張三" , "李四" ], //引用類型 sayName: function (){ alert( this .name) } }; var q1= new Ques(); var q2= new Ques(); q1.friends.push( "王五" ); alert(q1.friends); // alert(q2.friends); // alert(q1.friends===q2.friends); //相信大家已經看到了問題,當我創建了兩個實例q1、q2,當我為q1的“朋友”添加了“王五”之后,q2的”朋友“也有了三個張三、李四、王五 //那是因為數組存在于Ques.prototype上,而非q1上。所以出現了如上結果。 //而正是這個問題,我們很少看到有人單獨使用原型模式的原因所在。 </script> |
本文就先到這里了,后續我們再繼續討論javascript面向對象程序設計,希望大家能夠喜歡。