-
Notifications
You must be signed in to change notification settings - Fork 0
/
面向对象.html
297 lines (289 loc) · 16.5 KB
/
面向对象.html
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
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>面向我的对象 Object Oriented</title>
<script>
//理解对象是个啥玩意 每个对象都是基于一个引用类型创建的,这个引用类型可以是第五章讨论
//的原生类型 ,也可以 是开发人员定义的类型。
// var person= new Object();
// person.name="Nicholas";
// person.age=29;
// person.job="Software Engineer";
// person.sayName=function(){
// alert(this.name)
// };//第一种创建方法
// var person ={
// name:"Nicholas",
// age:29,
// job:"Software Engineer",
// sayName:function(){
// alert(this.name);
// }
// }
//第二种方法
//属性类型 :ES5所定义只有在内部才用的特性(attribute),描述了属性(property)的各种特性。
//ES定义这些特性是为了实现Javascript引擎用的,因此在js中不能直接访问他们。为了表示特性是内部值,该规范就把它们放在双对[]中 如[[Enumberalbe]] 该教材参考第五版的描述。 ES中两种属性:数据属性与访问器属性。
//1,数据属性包含一个数据值的位置,在这个位置可以读取和写入值。数据属性有4个描述其行为的特性
//1 [[Configuralbe]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问属性。像前面例子中那样直接在对象定义的属性,他们的这个特性默认为true.
//2[[Enumberale]]:表示能否通过for-in循环返回属性。像前面例子那样直接在对象上定义的属性,他们的这个特性默认值为true.
//[[writable]]:表示能否修改属性的值。像前面例子中那样直接在对象上定义的属性,他们的这个默认特性为true。
//[[Value]]:包含这个属性的数据值。读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。这个特性值默认为undefined。
// var person ={
// name:"Nicholas";
// }//这个为创建了[[Value]]的值 要修改属性默认值的特性,必须使用ES5中Object.defineProperty()方法。这个方法接受三个参数:属性所在对象,属性的名字,一个描述符对象。其中描述符(descriptor)对象的属性值必须为:configuralbe,enumberale,wirtable,value。设置其咋红一个或者多个值,可以修改对应的特性值。
// var person={};
// Object.defineProperty(person,"name", {
// writable:false,
// value:"Nicholas",//不可修改
//
// });
// alert(person.name);
// person.name="Greg";
// alert(person.name)
// //类似的规则也适用于不可配置的属性。例如:
// var person={};
// Object.defineProperty(person,"name",{
// configurable:false,
// value:"Nicholas"
// });
// alert(person.name);
// delete person.name;//因为configurable已经设置了 所以你删不了啊
// alert(person.name);
//这个4个内置的鬼方法在IE吧这个撒旦面前是有限制的
// 访问器属性:
//1 [[Configuralbe]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问属性。像前面例子中那样直接在对象定义的属性,他们的这个特性默认为true.
//2[[Enumberalbe]] :表示能否通过for-in 循环返回属性。对于直接在对象上定义的属性,这个特性的默认值为true.
//3[[Get]]:在读取属性时调用的函数。默认值undefined.
//4 [[Set]]:在写入属性时的调用的函数。默认值为undefined。
// var book={
// _year:2004,
// edition:1
// };
// Object.defineProperty(book,"year",{
// get:function(){
// return this._year;
// },
// set:function(newValue){
//
// if(newValue>2004){
// this._year=newValue;
// this.edition += newValue-2004
// }
//
// }
// });
// book.year=2005;//_表示只能通过对象方法访问的属性。将year修改为2005会导致_year的值发生表变化。
// alert(book.edition);
// 访问器属性中自带getter 与setter。只指定getter以为着属性不能写,尝试写入属性会被忽略。支持ES5这个方法的浏览器有IE9+ IE8部分实现 Firefox4+,Chrome.在这个方法之前,要创建访问器属性,一般都使用两个非标准的方法:__defineGetter__()和__defineSetter__()。这两个方法最初由Firefox引入的,后来其他浏览器也给出了相同的实现。使用这两个遗留方法可以重写前面的代码。
// var book ={
// _year:2004,
// edition:1
// };
// book.__defineGetter__("year",function(){
// return this._year;
// });
// book.__defineSetter__("year",function(newVlaue){
// if(newVlaue>2004){
// this._year=newVlaue;
// this.edition+=newVlaue-2004
// }
// });
// book.year=2005;
// alert(book.edition);
//在不支持Object.defineProperty()方法的浏览器不能修改[[Configuralbe]]与[[Enumberable]]
//定义多个属性
//以下代码在book对象上定义了两个数据属性(_year和edition)和一个访问器属性(year)。最终对象与上一节中定义的对象相同。唯一的区别是这里的属性都是同一时间创建的。
//
// var book={};
// Object.defineProperties(book,{
// _year:{
// writable:true,
// value:2004
// },
// edition:{
// writable:true,
// value:1
// },
// year:{
// wirable:true,
// value:1
// },
// year:{
// get:function(){
// return this._year;
// },
// set:function(newVlaue){
// if(newVlaue>2004){
// this._year=newVlaue;
// this.edition+=newVlaue-2004
// }
// }
// }
// });
// 读取属性的特性 ES5中Object.getOwnPropertyDecriptor()方法,可以取得给定属性的描述符。这个方法接受两个参数:属性所在的对象和要读取其描述符的属性名称。返回值一个对象,如果访问器属性,这个属性有Configuralbe,Enumberable,Get,Set。如果是数据属性,这个对象属性就有Configurable,enumerable,writable,value。
// var book={};
// Object.defineProperties(book,{
// _year:{
// value:2004
// },
// edition:{
// value:1
// },
// year:{
// get:function(){
// return this._year;
// },
// set:function(newValue){
// if(newValue){
// this._year=newValue;
// this.edition+=newValue-2004;
// }
// }
// }
// });
// var descriptor =Object.getOwnPropertyDescriptor(book,"_year");
// alert(descriptor.value);//2004
// alert(descriptor.configurable);//false
// alert( typeof descriptor.get);//undefined
// var descriptor =Object.getOwnPropertyDescriptor(book,"year");
// alert(descriptor.value);//undefined
// alert(descriptor.enumerable);// false
// alert(typeof descriptor.get)// function
//工场模式:考虑到JS中无法创建类,开发人员就发明了一种函数,用函数来封装特定接口创建对象的细节,如下面的例子所示。
// function createPerson(name,age,job){
// var o =new Object();
// o.name=name;
// o.age=age;
// o.job=job;
// o.sayName=function(){
// alert(this.name);
// };
// return o;
// }
// var person1=createPerson("Nicholas",29,"Software Engineer");
// var person2=createPerson("Greg",27,"Doctor")
//工场模式虽然解决了创建多个类似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。随着Javascript的发现,又一个新模式出现了。
//构造函数
//经过前几章的介绍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 Enginner");
// var person2=new Person("Greg",22,"Doctor");
// person1.sayName()//当做构造函数使用
// alert(person1.constructor == Person);
// alert(person2.constructor == Person);
// alert(person1 instanceof Object);
// alert(person1 instanceof Person)
// alert(person2 instanceof Object)
// alert(person2 instanceof Person)
// 将构造函数当成函数 ,构造函数与其他函数的唯一区别,就是调用他们的方式不同。不过,构造函数毕竟也是函数,不存在定义构造函数的特殊语法。任何函数,只要通过new操作符来调用,那它跟普通函数也不会有什么两样。例如前面的Person()函数可以通过下列任何一种方式来调用。
//var person =new Person("Nicholas",29,"Software Engineer");
//person.sayName();
//当做构造函数调用
//Person("Greg",27,"Doctor");
//window.sayName()//作为一个普通函数调用
//var o =Object();
//Person.call(o,"Kirsten",25,"Nurse");//相当于属性添加在o中后然后丢给Person
//o.sayName();//在另一个对象作用域中调用
//这个例子中的前两行代码展示了构造函数的典型用法,即使用new操作符来创建一个新对象。接下来的两行代码展示了不使用new操作符调用Person()会出现什么结果;对象和方法都被添加给window对象了。有人可能会记得,当在全局作用域中调用一个函数时,this对象总是指向Global对象(在浏览器就指向Window对象)。因此,在调用完函数之后,可以通过window对象来调用sayName()方法,并还返"Greg"。最后,也可以使用call()(或者apply())在某个特殊对象的作用域中调用Person()函数。这里是在对象o的作用域中调用,因此调用后o就有了所哟普属性和sayName的方法
//构造函数的一些问题,从逻辑角度出发。构造函数也可以用这种方式创建
// function Person(name,age,job){
// this.name=name;
// this.age=age;
// this.job=job;
// this.sayName=new Function("alert(this.name)");//等价于function(){ alert(this.name)}
// }
// 从这个角度上来看构造函数,更容易明白每个Person实例都包含一个不同的Function实例(以显示name属性)的本质。说明白些,以这种方式创建函数,就会导致不同的作用域和标示符解析,但创建Function新实例的机制依然是相同的。因此不同实例上的同名函数不相等,以下代码将会证明这一次点。
// alert(person1.sayName===person2.sayName)// false
//然后,创建两个完成同样任务的Function实例的确没有必要;况且有this对象在,根本不用在执行代码前就把函数绑定在特定对象上面。因此,大可像下面这样,通过把函数定义转移到构造函数外部解决这个问题。
// function Person(name,age,job){
// this.name=name;
// this.age=age;
// this.job=job;
// this.sayName=sayName;
//
// }//将sayName的函数转移在外部执行。
// function sayName(){
// alert(this.name)
// }
// var person1 =new Person("Nicholas",29,"Software Enigneer");
// var person2 =new Person("Greg",28,"Doctor");
//相当于设置为一个全局的sayName。 这样一来,由于sayName包含的是一个指向函数的指针,因此person1和person2对象就共享了在全局作用域中定义的同一个sayName()函数。这样就解决了两个函数做同一件事情的问题。但我们也引来了新的问题:在全局作用域中定义的函数实际上只能被某个对象调用,这让全局作用域有点名不副实。而更让人无法接受的是:如果对象需要定义很多方法,那么就要定义很多全局函数,于是我们这个自定义的引用类型就丝毫没有封装性可言了(都在全局调用了 还封个J*函数)。好在,这些问题可以通过使用原型模式来解决。
//原型模式
// 我们创建的每一个函数都有一个prototyp属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。如果按照字面意思来理解,那么prototype就是通过调用构造函数而创建的那个对象实例的原型对象。使用原型对象的好处是可以让所有对象实例共享它所包含的属性的方法。换句话,不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加原型对象中,如下面的例子所示。
function Person(){
}
Person.prototype.name="Nicholas";//Person.prototype.constructor
Person.prototype.age=29;
Person.prototype.job="Software Enginner";
Person.prototype.sayName=function(){
alert(this.name);
}
var person1 =new Person();
person1.sayName();
var person2 =new Person();
person2.sayName();
alert(person1.sayName== person2.sayName)//ture
// 理解原型对象
// 无论何时,只要创建一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,该属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor属性,这个属性是指向prototype属性所在的指针。 通过这个构造函数,我们可以继续为原型对象添加其他属性跟方法。
// /ES5中管这个指针叫[[Prototype]] 国内Fire和Chrome 都支持一个属性__proto__;而这个属性对脚本则是完全不可见的。不过要明确的真正重要的一点是,这个连接存在于实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间。 仅仅是起连接作用。
// 虽然所有的实现中都无法访问到[[Prototype]],但可以通过isPrototypeOf()方法来确立对象之间是否存在这种关系。从本质上讲,如果[[Prototype]]指向调用isPrototypeOf方法的对象(Person.prototype),那么糟糕方法就会返回true;
alert(Person.prototype.isPrototypeOf(person1));
alert(Person.prototype.isPrototypeOf(person1));
//ES5中又添加了一个新方法,叫Object.getPrototypeOf(),在所有支持的实现中,这个方法返回[[Prototype]]的值。
alert(Object.getPrototypeOf(person1) ==Person.prototype)
alert(Object.getPrototypeOf(person1).name);
// 关于新添加的属性将覆盖原型的属性 这里先不讲 讲将如何使用hasOwnProperty()
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"));
person1.name="Greg";
alert(person1.name);
alert(person1.hasOwnProperty("name"));
alert(person2.name);
alert(person2.hasOwnProperty("name"));
delete person1.name;
alert(person1.name);
alert(person1.hasOwnProperty("name"))
// ES5中的Object.getOwnPropertyDescriptor()方法只能用于实例属性,要取得原型属性的描述符,必须直接在原型对象上调用Object.getOwnProperty-Descriptor.
//原型与in的操作符 有两种方式使用in操作符:单独使用和在for- in循环中使用。在单独使用时,in操作符会在通过对象能够范文给定属性时返回true,无论该属性存在于实例中还是原型中。看看下面的例子。
function Person(){
}
Person.prototype.name="Nicholas";
Person.prototype.age=29;
Person.prototype.job="Software Enginner";
Person.prototype.sayName= function(){
alert(this.name);
};
var person1 =new Person();
var person2 =new Person();
alert(person1.hasOwnProperty("name"));//false
alert("name"in person1);//true 实用性比hasOwnProperty要广
person1.name="Greg";
alert(person1.name);//Greg
alert(person1.hasOwnProperty("name"));//false
alert("name" in person2);//true
delete person1.name;
alert(person1.name);//"Nicholas"从原型中查找
alert(person1.hasOwnProperty("name"));//false
alert("name" in person1)//true
</script>
</head>
<body>
</body>
</html>