javascript面向对象(二)--创建对象

  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);  //浏览器会忽略这条语句 

 

posted on 2017-09-14 02:10  zhd_七  阅读(187)  评论(0编辑  收藏  举报