-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathObject Oriented.html
258 lines (257 loc) · 10.6 KB
/
Object Oriented.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
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Object Oriented</title>
</head>
<body>
创建对象:工厂模式、构造函数模式、原型模式、组合使用构造函数模式和原型模式、动态原型模式、寄生构造函数模式、稳妥构造函数模式...
</body>
<script>
/*
对象是一组没有特定顺序的值;每个对象都是基于一个引用类型创建的;对象的每个属性或方法都有一个名字,而每个名字都映射到一个值...
数据属性:
Configurable[true]: 能否通过 delete 删除属性从而重新定义属性
能否修改属性的特性
能否把属性修改为访问器属性
Enumerable[true]: 能否通过 for-in 循环返回属性
Writable[true]: 能否修改属性的值
Value[undefined]: 包含这个属性的数据值
访问器属性:Configure、Enumerable、Get、Set
读取访问器属性时,会调用 getter 函数,这个函数负责返回有效的值
写入访问器属性时,会调用 setter 函数并传入新值,这个函数负责决定如何处理数据
Get:在读取属性时调用的函数。默认值为 undefined
Set:在写入属性时调用的函数,默认值为 undefined
*/
var sampleObj = {
name: 'sample',
age: 18
};
Object.defineProperty(sampleObj, 'name', {
configurable: false,
enumerable: false,
writable: false,
value: 'pynk'
});
try {
// 不能重新定义
Object.defineProperty(sampleObj, 'name', {
value: 'p!nk'
});
} catch (error) {
sampleObj.name = 'hahasha';
}
delete sampleObj.name;
console.log(sampleObj); // 可以访问,没有被修改,没有被 delete 掉...
for (prop in sampleObj) {
console.log(prop); // 没有 name 属性...
}
Object.defineProperty(sampleObj, 'age', {
get: () => {
return this.age * 2;
},
set: value => {
this.age = value > 100 ? 100 : value;
}
});
sampleObj.age = 101; // set
console.log(sampleObj); // get
var sampleWiz = new Object();
Object.defineProperties(sampleWiz, {
name: {
value: 'Knowles'
},
sex: {
// value: 1, ---> 会报错...
set: () => {
try {
alert("Don't Change My Sex...");
} catch (error) {
this.value = 1;
}
}
},
job: {
// value: "fontEndProgrammer", ---> 会报错...
get: () => {
return "I Don't Wanna Tell You...";
}
}
});
sampleWiz.sex = 0;
sampleWiz.age = 24;
console.log(sampleWiz);
console.log(sampleWiz.job);
// 读取数据属性
console.log(Object.getOwnPropertyDescriptor(sampleWiz, 'name'));
// 读取访问器属性
console.log(Object.getOwnPropertyDescriptor(sampleWiz, 'sex'));
// 读取数据属性
console.log(Object.getOwnPropertyDescriptor(sampleWiz, 'age'));
/*(⊙ˍ⊙)(⊙ˍ⊙)(⊙ˍ⊙)(⊙ˍ⊙)(⊙ˍ⊙)(⊙ˍ⊙)(⊙ˍ⊙)(⊙ˍ⊙)(⊙ˍ⊙)(⊙ˍ⊙)(⊙ˍ⊙)(⊙ˍ⊙)(⊙ˍ⊙)*/
// 工厂模式,没有解决对象识别的问题...
function createPerson(name, age, job) {
let p = new Object();
p.name = name;
p.age = age < 1 ? 1 : age;
p.job = job;
p.introduce = function () {
alert("Hi, I'm " + this.name + '...');
};
return p;
}
var spears = createPerson('Spears', 18, 'singer');
spears.introduce();
// 构造函数模式
function Person(name, age, job) {
/*
没有显式地创建对象
直接将属性和方法赋给了 this 对象
没有 return 语句
*/
this.name = name;
this.age = age < 1 ? 1 : age;
this.job = job;
// 每个方法都要在每个实例上重新创建一遍...这是缺点...
this.introduce = function () {
alert("Hi, I'm " + this.name + '...');
};
this.howOlder = howOlder;
}
function howOlder() {
/*
在全局作用域中定义的函数实际上只能被某个对象调用,这让全局作用域有点名不副实;
如果对象需要定义很多方法,那么就要定义很多个全局函数,自定义的引用类型就没有封装性可言了。
*/
console.log("Hi, I'm " + this.age + '...');
}
/*
任何函数,只要通过 new 操作符来调用,那它就可以作为构造函数;
任何函数,如果不通过 new 操作符来调用,那它跟普通函数没有什么两样;
创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型...
创建一个新对象;
将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);
执行构造函数中的代码(为这个新对象添加属性);
返回新对象。
*/
var swift = new Person('Swift', 17, 'farmer');
console.log(swift);
console.log(swift.constructor === Person); // true
console.log(swift.constructor === Object); // false
console.log(swift instanceof Person); // true
console.log(swift instanceof Object); // true
// 在 noName 的作用域中调用
var noName = {};
Person.apply(noName, ['noName', 1, 'kid']);
console.log(noName);
console.log(noName.introduce == swift.introduce); // false
console.log(noName.howOlder == swift.howOlder); // true
/*
原型模式
原型通过调用构造函数而创建的那个对象实例的原型对象...
让所有对象实例共享它所包含的属性和方法...
不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中...
在默认情况下,所有原型对象都会自动获得一个 constructor(构造函数)属性,这个属性包含一个指向 prototype 属性所在函数的指针...
*/
function Protoz() {}
Protoz.prototype.name = 'Nicjolas';
Protoz.prototype.age = 28;
Protoz.prototype.say = function () {
alert(this.name);
};
var sapleOne = new Protoz(),
sapleTwo = new Protoz();
sapleOne.sex = 1;
sapleTwo.sex = 1;
console.log(sapleOne === sapleTwo); // false
console.log(sapleOne.say === sapleTwo.say); // true
console.log(Protoz.prototype.isPrototypeOf(sapleOne)); // true
console.log(Protoz.prototype === Object.getPrototypeOf(sapleTwo)); // true
/*
原型链
--->
每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。
搜索首先从对象实例本身开始。如果在实例中找到了具有给定名字的属性,则返回该属性的值;
如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性。
如果在原型对象中找到了这个属性,则返回该属性的值。
--->
当为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性;
添加这个属性只会阻止我们访问原型中的那个属性,但不会修改那个属性;
使用 delete 操作符则可以完全删除实例属性,从而重新访问原型中的属性。
*/
console.log(sapleOne.hasOwnProperty('name')); // false --- sapleOne 的 name 属性不再实例中,而是存在于原型链中...
console.log(sapleTwo.hasOwnProperty('sex')); // true --- sex 存在于实例中,是属于自己的属性...
/*
in 操作符:
单独使用 ---> 通过对象能够访问给定属性时返回 true,无论该属性存在于实例中还是原型中...
for···in ---> 返回所有能够通过对象访问的、可枚举的属性,既包括存在于实例中的属性,也包括存在于原型中的属性...
*/
console.log('sex' in sapleOne); // true
console.log('age' in sapleTwo); // true
for (item in sapleOne) {
console.log(item); // sex、name、age、say
}
console.log(Object.keys(sapleTwo)); // ["sex"] --- 取得对象上所有可枚举的实例属性
console.log(Object.getOwnPropertyNames(sapleTwo)); // ["sex"] --- 取得所有实例属性,无论他是否可枚举
// 原型对象的缺点
function Teenager() {}
Teenager.prototype = {
constructor: Teenager,
name: 'Wiz',
age: 28,
friends: ['Perry', 'Lewis'],
callFriends: function () {
console.log('My First Friend: ' + this.friends[0]);
}
};
var t1 = new Teenager();
var t2 = new Teenager();
t1.friends.push('Vaps');
// friends 属性是引用类型,对 t1 的修改影响了 t2...
console.log(t2.friends);
console.log(t1.friends === t2.friends); // true
/*
构造函数与原型模式混合(用的最广):
构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性;
每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用。
*/
function Human(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.friends = ['Burke', 'Smith'];
}
Human.prototype = {
constructor: Human,
sayHi: function () {
console.log("Hi, I'm " + this.name);
}
};
var h1 = new Human('Goge', 18, 'Dancer');
var h2 = new Human('Lott', 24, 'Singer');
h1.friends.push('Cole');
h2.friends.push('Brown');
console.log(h1.friends === h2.friends); // false
console.log(h1.sayHi === h2.sayHi); // true
// 动态原型模式
function Active(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
if (typeof this.sayName != 'function') {
Active.prototype.sayName = function () {
alert(this.name);
};
}
}
var activeSample = new Active('hahasha', 25, 'fed');
console.log(activeSample);
/*
寄生构造函数模式,和工厂模式很像,不做介绍...
稳妥构造函数模式,不引进 this、不使用 new 操作符...
*/
console.log('to be continued...');
</script>
</html>