javascript面向对象(二)

时间:2021-07-30 15:36:58
  1 /创建对象:
2 //最基本的 Object构造以及字面量法
3 //一、工程模式创建:使用参数传值在构造
4 function createObject(name,age,job){
5 var o = new Object();
6 o.name = name;
7 o.age = age;
8 o.job = job;
9 o.sayName = function(){
10 alert(this.name);
11 };
12 return o;
13 }
14 var person = createObject('zhd',23,'Software Engineer');
15 //该方式无法确定 该对象是一个什么样的类型(都是有Object创建的)
16
17 //二、构造函数模式:改进工厂模式
18 function Person(name,age,job){ //约定俗成 首字母大写
19 var o = new Object();
20 this.name = name;
21 this.age = age;
22 this.job = job;
23 this.sayName = function(){
24 alert(this.name);
25 };
26 //也可以像下面这种方式定义,这种方式定义会导致不同的作用域链和标识符解析,例:
27 //alert(person1.sayName == person2.sayName) //false
28 //但创建机制还是一样的
29 //this.sayName = new Function("alert(this.name"); //没有大括号
30 }
31 var person = new Person('zhd',23,'Software Engineer'); //构造方法
32 //每个对象都有一个 constructor指向构造函数,因此
33 alert(person.constructor == Person); // true
34 alert(person instanceof Person); //true
35 alert(person instanceof Object); //true 所有的对象均继承自Object类
36
37 //构造函数也是函数,所有用new出来的函数,都可以做构造函数来看待
38 var person = new Person('zhd',23,'Software Engineer');
39 person.sayName(); // zhd
40
41 Person('qi',23,'Software Engineer'); //普通函数来看待
42 window.sayName(); // qi 只能是Global(浏览器时window)调用
43
44 var o = new Object();
45 Person.call(o,'qi',23,'Software Engineer'); //普通函数来看待
46 o.sayName(); // qi 在 对象o中调用
47
48 //可以把sayName()对象转移到函数体外部,this指针的存在,可以保证在构造函数调用外面的sayName()时,
49 //两个对象不会产生混乱
50 function Person(name,age){
51 this.name = name;
52 this.age = age;
53 this.sayName = sayName;
54 }
55 function sayName(){alert(this.name);}
56
57 //三、原型方法
58 //对于构造函数模式来讲,可以把对象中若干个函数移出构造函数体外部,成为全局变量。
59 //然而,这些函数并不是真正意义上的全局变量,大多数情况下仍然是声明出来的某个对象所调用
60 //况且,方法过多让大量的方法在作为全局变量游离在构造方法之外,破坏了封装型
61 //可以使用原型模式来解决
62
63 //创建的每个函数都有一个 prototype属性,这是一个指针,指向一个对象,这个对象的用途就是
64 //包含可以由特定类型的所有实例共享的属性和方法
65 //通过调用构造函数而创建那个对象实例的原型对象
66 //好处就是让所有对象实例共享它所包含的属性和方法
67 //也就是说 不必在构造函数里定义对象的信息,可以将信息直接添加到原型对象中去
68 function Person(){};
69 Person.prototype.name = "zhd";
70 Person.prototype.age = 23;
71 Person.prototype.sayName = function(){
72 alert(this.name);
73 };
74
75 var person1 = new Person();
76 person1.sayName(); //zhd
77 var person2 = new Person();
78 person1.sayName(); //zhd //所有实例共享属性和方法,与构造函数模式不同
79
80 alert(person1.sayName == person2.sayName); //true 没有(),这里比较的是两个指针
81
82 //理解原型对象 (这个对象可以理解为实实在在存在的一个对象,并且与这个函数息息相关)
83 //只有创建一个函数,就会根据一组特定的规则为这个函数创建一个prototype属性指向函数的原型对象。
84 //默认情况下,所有的原型对象都会自动获得一个constructor属性,这个属性包含一个指向prototype属性
85 //所在函数的指针。例如:Person.prototype.constructor指向Person。
86 //创建自定义构造函数后,其原型对象默认只会获取constructor属性。至于其他方法都是Object继承而来
87 //每创建一个新实例,该实例都会包含一个指针指向构造函数的原型对象。
88 //ECMA-262第五版称之为 [[prototype]]
89
90 //它们之间的关系就是:
91 //每个新实例会有一个指针指向构造函数的原型对象。
92 //给构造函数的原型对象赋值。
93 //也就是说每个新实例都会得到原型对象中属性的值。
94 //因此 alert(person1.sayName == person2.sayName)返回true
95 //所有的实例共享构造函数的原型对象中的属性和方法
96
97 //可以通过 isPrototypeOf() 方法来确定是否有这么一层关系
98 alert(person.prototype.isPrototypeOf(person1)); //true
99
100 //ECMAScript 5中的 Object.getPrototypeOf() 可以很方便的获取一个实例的原型对象
101 alert(Object.getPrototypeOf(person1) == Person.prototype); //true
102 alert(Object.getPrototypeOf(person1).name); //zhd
103
104 //程序在读取实例属性的时候,会先后搜索两次属性的名称 以 name为例
105 //第一次在实例person1中查找name
106 //如果未找到,就会在person1的原型对象中查找name
107 //因此可以直接在实例中,定义一个与源性对象中属性相同的名字,程序就会屏蔽原型对象中的值
108 //只是在这个实例中屏蔽,不会改变原型对象的值
109 var person3 = new Person();
110 person3.name = 'zhdqi';
111 alert(person3.name); //zhdqi
112 alert(person1.name); //zhd 不会改变
113
114 //delete person3.name;
115 //alert(person3.name); //zhd 删掉实例同名的属性后,就会继续访问原型对象中的值
116
117 //可以使用 hasOwnProperty(); 判断访问的对象属性来自实例还是来自原型对象
118 alert(person1.hasOwnProperty("name")); //false 来自原型
119 alert(person3.hasOwnProperty("name")); //true 来自实例对象
120
121 //in操作符:可以确定在实例中能否访问某个属性,无论属性是实例还是原型对象
122 alert("name" in person1); //true name来自原型对象 只要能访问到 就是true
123 alert("name" in person3); //true name来自实例本身
124
125 //同时使用hasOwnProperty和in能够确定访问的属性的值来自与原型对象还是实例本身
126 function hasPrototypeProperty(object,name){
127 return !hasOwnProperty(name)&&(name in object);
128 }
129 alert(hasPrototypeProperty(person1,'name')); //true 能访问 且来自原型对象
130 alert(hasPrototypeProperty(person3,'name')); //true 能访问 来自实例本身
131
132 //for-in能够访问对象中可枚举的属性(不论属性来自实例还是原型对象)
133 //如果原型对象某属性屏蔽了可枚举性,但实例中重新定义了该属性,那么for-in也是可以访问的
134 //使用 Object.keys(),会返回一个对象中所有可枚举实例属性的字符串数组 注意:是实例属性
135 alert(Object.keys(person.prototype)); //'name','age','job'
136 alert(Object.keys(person3)); //'name' 实例对象的属性
137
138 //Object.getOwnPropertyNames(Person.pertotype) 返回所有实例的属性
139 alert(Object.getOwnPropertyNames(Person.pertotype)); //constructor,name,age,job,sayName
140
141 //更简单的语法
142 function Person(){}
143 Person.prototype = {
144 name:'zhd',
145 age:23,
146 job:'Software Engineer',
147 sayName:function(){alert(this.name);}
148 }
149
150 //!!!这样相当于重写了 prototype对象,因此 constructor指向新的constructor对象(Object)
151 //原型对象是一个已经存在的对象,因此可以在上面随意的增加删除属性与方法
152 //但是使用字面量法重新定义之后,就相当于重写该方法
153 //使用instanceof操作符还会有正确的结果,但是constructor无法确定对象的类型了
154 var friend = new Person();
155 alert(friend instanceof Object); // true
156 alert(friend instanceof Person); // true
157 alert(friend.constuctor == Person); // false
158 alert(friend.constuctor == Object); // true
159 //假如constuctor值很重要
160 Person.prototype = {
161 //constuctor:Person, //可以直接指定 但这样会变成可枚举类型,而默认是不可枚举的
162 name:'zhd',
163 age:23,
164 job:'Software Engineer',
165 sayName:function(){alert(this.name);}
166 }
167 Object.defineProperty(person.prototype,"constructor",{
168 enumerable:false, //重设constructor,使其变成不可枚举的值
169 value:Person
170 });
171
172 //实例与构造函数之间是一种松散关系,可以在原型对象上任意定义与删除方法都可以反映在实例上
173 //pertotype作为实例与构造函数之间的桥梁
174 //一旦重写了pertotype,就相当于切断了这层关系
175 function Person(){}
176 var friend = new Person();
177 Person.prototype = { //重写了方法 桥梁被切断
178 constuctor:Person,
179 name:'zhd',
180 age:23,
181 job:'Software Engineer',
182 sayName:function(){alert(this.name);}
183 }
184 alert(friend.sayName); // undefined 他引用的依旧是最原始的原型对象
185
186 //原生对象的原型:即 Object,Array,String等原生引用类型的方法也是可以在其原型对象上找到
187 //例如 可以在 Array.prototype找到sort()方法,也可以在String.prototype中找到substring方法
188
189 //通过原生对象的原型,可以在原生类型上随时添加新的方法
190 //例 在String添加一个 startsWith()
191 String.prototype.startsWith = function(text){
192 return this.indexOf(text) == 0;
193 }
194 var msg = "hello world";
195 alert(msg.startsWith('hello')); //true
196
197 //原型对象的局限性
198 //这种创建对象的方式是基于共享的,在原型对象中,每一个实例都可以重新定义(修改)原型中已有的属性
199 //且不会共享。但是,如果原型中带有引用类型的变量,例如,原型中有一个数组,那么在对数组进行操作而没有
200 //重新定义的时候,那么这个被操作的数组就会被所有实例所共享
201 function Person(){}
202 Person.prototype = { //重写了方法 桥梁被切断
203 constuctor:Person,
204 name:'zhd',
205 age:23,
206 likecolor:['red','green'],
207 job:'Software Engineer',
208 sayName:function(){alert(this.name);}
209 }
210 var person1 = new Person();
211 var person2 = new Person();
212 person1.likecolor.push('blue'); //这里会共享
213 alert(person1.likecolor); //red green blue
214 alert(person2.likecolor); //red green blue
215 alert(person1.likecolor == person2.likecolor); //true
216
217 //四、结合构造函数模式以及原型对象模式创建对象 最流行的一种方法
218 //构造函数模式用于 定义实例属性
219 //原型对象模式 定义实例方法以及共享的属性
220 //每个实例都有自己的一份属性的副本,且共享着对方法的引用
221 function Person(name,age,job){
222 this.name = name,
223 this.age = age,
224 this.job = 'Software Engineer'
225 }
226 Person.pertotype = {
227 constuctor:Person,
228 sayName:function(){
229 alert(this.name);
230 }
231 }
232 //五、动态原型模式
233 //所谓动态原型,就是追求一种封装的效果。将原型对象的方法放到了构造函数中,通过if判断的方式动态的加载原型对象的方法
234 //判断只需要判断其中一个方法(如果有若干个方法的话),作用就是判断构造函数是否已经加载了原型对象中的方法
235 //与原型模式一样,使用字面量法重写原型会切断实例与构造函数之间的桥梁
236 function Person(name,age,job){
237 this.name = name;
238 this.age = age;
239 this.job = job;
240
241 //if(Person.pertotype.method == undefined) /这样写也可以
242 if(typeof this.sayName != "function"){
243 //加载原型对象的函数到构造函数
244 Person.pertotype.sayName = function(){
245 alert(this.name);
246 };
247 }
248 }
249 //六、寄生构造函数模式 与工厂模式基本相同
250 function Person(name,age){ //函数名称不再是 createObject
251 var o = new object();
252 o.name = name;
253 o.age = age;
254 o.sayName = function(){
255 alert(this.name);
256 };
257 return o;
258 }
259 var person1 = new Person('zhd',23);
260 person1.sayName(); // zhd
261
262 //在特殊场合下使用这种方式,但无法通过 instanceof 确定对象的类型,与工厂模式类似
263 //比如创造一个具有一个额外方法的特殊数组,由于不能修改Array函数,那么可以这么做:
264 function SpecialArray(){
265 var array = new Array(); //先创建一个数组
266 array.push.apply(array,arguments); //调用apply方法接受SpecialArray的参数给array的push赋值,进而给数组赋值
267 array.toPipeString = function(){
268 return this.join("|");
269 };
270 return array; //返回数组
271 }
272 var colors = new SpecialArray("red","green","blue"); // red,green,blue
273 colors.toPipeString(); //red|green|blue
274
275 //七、稳妥构造函数模式 :与寄生构造模式类型,但 1.新创建的实例方法不引用this 2.不能使用new调用构造函数
276 //使用于较为安全的环境(禁止使用this于new)
277 //由于没有this和new,构造函数中的数据成员无法被外部访问,只能被函数体内定义好的方法来访问
278 //在外部添加方法时,不能使用this指针,也无法在方法体内引用函数的数据成员变量
279 function Person(name,age){
280 var o = new Object();
281 //定义若干方法
282 o.name = name;
283 o.age = age;
284
285 o.sayName = function () {
286 alert(name);
287 };
288 return o;
289 }
290 var friend = Person('zhd',23);
291 friend.sayName(); //zhd
292 //alert(friend.name); //浏览器会忽略这条语句