首页 > 代码库 > js复杂创造面向对象方法
js复杂创造面向对象方法
学了这么久还是对面向对象有点模糊,所以今天就再写一点关于面向对象的
function Box(){}
var box=new Box();
alert(box.prototype) //undifined 使用对象实例无法访问到prototype
alert(box.__proto__);//object object 使用对象实例访问prototype的指针
alert(Box.prototype);//object object 使用构造函数名(对象名)访问prototype
说明对象的实例是无法直接通过prototype属性访问该构造方法的原型的,但是对象实例可以通过访问prototype的指针的方式来访问,同时构造函数(即对象名) 访问prototype。
二、为了让属性和方法更好的体现封装效果,并减少不必要的输入,原型对象也可以用字面量的方式来创建,代码如下
function Box(){}
Box.prototype={ ①
name:‘Lee‘,
age:‘27‘,
run:function(){
return this.name+this.age;
}
}
注意:①处的{}其实就是Object,new Object就相当于{}
使用构造函数创造源性对象与字面量创造原型对象在使用上基本相同,但是有一些区别,那就是 用字面量的方式 使用constructor属性不会指向实例(Box),而是会指向Object,构造函数的方式则不会出现这个问题,会指向(Box),具体看下面代码
var box=new Box();
alert(box.constructor)//function Object{}
ps:字面量的方式为什么会是constructor属性指向Object呢?那是因为,Box.prototype={}这种方式其实是创建了一个新对象,而每创建一个对象,就会同时创建他的prototype对象,这个对象也会自动获取constructor属性,所以新对象的constructor重写了Box原来的constructor,所以会指向新对象,那个新对象没有指定构造函数,所以就默认为Object了
当然也有办法让他的constructor重新指向Box,看如下代码
function Box(){}
Box.prototype={
constructor:Box, //强制指向Box
name:‘Lee‘,
age:‘27‘,
run:function(){
return this.name+this.age;
}
}
var box=new Box();
alert(box.constructor)//function Box{}
用constructor:Box,就会让他重新 指向 Box实例
三、原型的声明是有先后的,所以重写的原型,会切断之前的原型
function Box(){}
Box.prototype={
constructor:Box, //强制指向 Box
name:‘Lee‘,
age:‘27‘,
run:function(){
return this.name+this.age;
}
}
Box.prototype={ //重写了原型对象,这里不会保留之前的原型的任何信息
age:‘100‘
}
var box=new Box();
alert(box.name);//undifined
这个很好理解,第二次Box.prototype把第一次的给重写了,表面上是改了 age的值,其实是 把整个原型都重写了,所以上面的原型彻底与构造函数实例切断,box.name在第二次根本不存在,因此,undifined也是自然而然的事情了
// //查询sort是否是Array源性对象里的方法
// alert(Array.prototype.sort);
// alert(String.prototype.substring);
// 给String添加方法
String.prototype.addString = function() { //尽管给原生的内置引用类型添加方法特别方便,但我们不推荐这种方法
//因为他可能导致命名冲突,不利于代码维护
return this + "被添加了、、、";
}
alert("xixida".addString()); //xixida被添加了、、、
原型+构造方法
一、我们都知道原型模式构建对象的最大优点就是共享,但是你知道吗,原型模式最大的缺点就是它最大的优点,如果共享的是方法的话使我们期望的一般也不会有什么影响,但是如果牵扯到其他的引用类型的话就会出现麻烦,看如下;
//原型的缺点 function Box() {} Box.prototype={ constructor:Box, name:‘Lee‘, age: 20, family:[‘哥哥‘,‘姐姐‘,‘妹妹‘] } var box1 = new Box(); var box2 = new Box(); alert(box1.family);//哥哥、姐姐、妹妹 box1.family.push(‘弟弟‘); 在第一个实例修改后的引用类型,保持了共享 alert(box1.family);//哥哥、姐姐、妹妹、弟弟 alert(box2.family)//哥哥、姐姐、妹妹、弟弟 //共享了box1添加后的引用类型
二、为解决这个问题,我们可以使用组合原型模式+构造函数
function Box(name, age) { //不共享的使用构造函数 this.name = name; this.age = age; this.family = [‘父亲‘,‘母亲‘,‘妹妹‘] } Box.prototype={ //共享的使用原型模式 constructor:Box, run:function(){ return this.name+this.age; } } var box1 = new Box(‘xixi‘,10); var box2 = new Box(‘Jack‘,20); alert(box1.family); //父亲、母亲、妹妹 box1.family.push(‘didi‘); alert(box1.family);//父亲、母亲、妹妹、didi alert(box2.family);//父亲、母亲、妹妹 引用类型没有使用原型,所以没有共享
ps:这种混合方式很好的解决了传参和引用共享的两大难题,是创建对象比较好的方法
三、上面的方式虽然一斤很完美了,但是 构造函数和原型模式 两块代码来 创造对象有点怪异,为了更好的体现封装性我们把它写在一起
//动态原型模式
//可以将原型封装到构造函数里 function Box(name, age) { //不共享的使用构造函数 this.name = name; this.age = age; this.family = [‘父亲‘,‘母亲‘,‘妹妹‘]; if(typeof this.run != "function"){ //原型的初始化,只要第一次初始化了就行了,没必要每次构造函数实例化的时候都初始化 Box.prototype.run=function(){ return this.age+this.name; } } }
如果以上的各种创建方式都不能满足你的需求,那就使可以尝试一下两种方式
①寄生构造函数=工厂模式+构造函数寄生.(构造函数模式和工厂模式没有本质区别,通过new 操作符的就叫寄生构造函数模式,直接调用的就叫工厂模式)
1 function Box(name,age){ 2 var obj = new Object(); 3 obj.name = name; 4 obj.age = age; 5 obj.run=function(){ 6 return this.name+this.age; 7 } 8 return obj; 9 }
寄生构造函数模式与工厂模式的区别是使用了new操作符把createPerson函数作为构造函数,其实是跟工厂模式一模一样的
②稳妥构造函数
1 // 在一些安全环境中,比附禁止使用this 和 new,(这里的this指的是不能在构造函数里面使用), 2 // new指的是不能在外部实例化构造函数的时候使用,这种方式叫做稳妥构造函数 3 function Person(name, age, job) { 4 var o = new Object(); 5 o.name = name; 6 o.age = age; 7 o.job = job; 8 o.sayName = function() { 9 alert(name); //注意这里没有了“this”; 10 }; 11 return o; 12 } 13 14 var person = Person("Nicholas", 29, "software Engineer"); 15 Person.sayName(); //"Nicholas"
稳妥构造函数采用的是与寄生构造函数模式相似的模式,除了下列两点不同:
1.创建对象的实例方法不引用this;
2.不使用new调用构造函数;
所以构造函数适合在禁用this和new的环境中使用(或者说设计的出发点)。
js复杂创造面向对象方法