JavaScript面向对象与原型(二)

深入探讨JavaScript的构造函数与原型模式
本文详细解析了JavaScript中构造函数和原型模式的使用,包括它们各自的特点、如何解决参数传递和属性共享的问题,以及如何通过动态原型模式和寄生构造函数进行优化。此外,文章还介绍了继承的概念及其在JavaScript中的实现方式,如原型链继承,并讨论了伪造对象和经典继承等技术。最后,文章展示了如何结合构造函数和原型模式进行组合继承,以解决实际开发中的常见问题。

 组合构造函数+原型模式

这种混合模式很好的解决了传参和引用共享的大难题。是创建对象比较好的方法。

 1 //不共享的使用构造函数
 2 function Box(name,age){
 3     this.name = name;
 4     this.age = age;
 5     this.family = ['父亲', '母亲', '妹妹'];
 6 };
 7 
 8 //共享的使用原型模式
 9 Box.prototype = {
10     constructor:Box,    
11     run : function(){
12         return this.age+" "+this.name+" "+this.family;
13     }
14 }

原型模式,不管你是否调用了原型中的共享方法,它都会初始化原型中的方法,并且在声明一个对象时,构造函数+原型部分让人感觉又很怪异,最好就是把构造函数和原型封装到一起。

为了解决这个问题,我们可以使用动态原型模式

 1 function Box(name,age){
 2     this.name = name;
 3     this.age = age;
 4     this.family = ['父亲', '母亲', '妹妹'];
 5     if(typeof this.run != "function"){
 6         Box.prototype.run = function(){
 7             return this.age+" "+this.name+" "+this.family;
 8         }
 9     }
10 };

当第一次调用构造函数时,run()方法发现不存在,然后初始化原型。当第二次调用,就不会初始化,并且第二次创建新对象,原型也不会再初始化了。
这样及得到了封装,又实现了原型方法共享,并且属性都保持独立。

PS:使用动态原型模式,要注意一点,不可以再使用字面量的方式重写原型,因为会切断实例和新原型之间的联系。


寄生构造函数

寄生构造函数,其实就是工厂模式+构造函数模式。这种模式比较通用,但不能确定对象关系,所以,在可以使用之前所说的模式时,不建议使用此模式。

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 }

 

在什么情况下使用寄生构造函数比较合适呢?假设要创建一个具有额外方法的引用类型。由于之前说明不建议直接String.prototype.addstring,可以通过寄生构造的方式添加。

1 function myString(string){
2     var str=new String(string);
3     str.addstring=function(){
4         return this+',被添加了!';
5     };
6     return str;
7 }
8 var box=new myString('Lee'); //比直接在引用原型添加要繁琐好多
9 alert(box.addstring());

在一些安全的环境中,比如禁止使用this和new,这里的this是构造函数里不使用this,这里的new是在外部实例化构造函数时不使用new。这种创建方式叫做稳妥构造函数。

1 function Box(name,age){
2     var obj=new Object();
3     obj.run=function(){
4         return name+age+'运行中...'; //直接打印参数即可
5     };
6     return obj;
7 }
8 var box=Box('Lee',100); //直接调用函数
9 alert(box.run());

PS:稳妥构造函数和寄生类似



继承

继承是面向对象中一个比较核心的概念。其他正统面向对象语言都会用两种方式实现继承:一个是接口实现,一个是继承。

而ECMAScript只支持继承,不支持接口实现,而实现继承的方式依靠原型链完成。

 1 function Box(){
 2     this.name = "Lee";
 3 }
 4 
 5 function Desk(){
 6     this.age = 30;
 7 }
 8 
 9 Desk.prototype=new Box();  //  Desk继承了Box 通过原型链条
10 var desk = new Desk();
11 console.info(desk.name+" "+desk.age); // Lee 30
12 
13 function Table(){
14     this.level = "j";
15 }
16 Table.prototype = new Desk();
17 var table = new Table();
18 console.info(table.name); // Lee

 

原型链继承流程图

如果要实例化table,那么Desk实例中有age=100,原型中增加相同的属性age=200,最后结果是多少呢?

1 Desk.prototype.age=200;   
2 console.info(table.age);  // 30  --->先查找构造函数实例里的属性或方法  如果构造函数实例里没有,则去它的原型对象里找

 

PS:以上原型链继承还缺少一环,那就是Obejct,所有的构造函数都继承自Obejct。而继承Object是自动完成的,并不需要程序员手动继承。以下为经过继承后的实例,他们的从属关系

1 console.info(table instanceof Object); //true
2 console.info(desk instanceof Table);   //false,desk是table的超类
3 console.info(table instanceof Desk);   //true
4 console.info(table instanceof Box);    //true

 

在JavaScript里,被继承的函数称为超类型(父类,基类也行,其他语言叫法),继承的函数称为子类型(子类,派生类)。继承也有之前问题,比如字面量重写原型会中断关系,使
用引用类型的原型,并且子类型还无法给超类型传递参数。

 

伪造对象、经典继承

为了解决引用共享超类型无法传参的问题,我们采用一种叫借用构造函数的技术,或者称为对象冒充(伪造对象、经典继承)的技术来解决这两种问题。

 1 function Box(age) {
 2     this.age = age;
 3     this.name = ['哥哥','姐姐','妹妹'];    //引用类型,放在构造里就不会被共享 
 4 }
 5 
 6 function Desk(age) {
 7     Box.call(this,  age)                //对象冒充,给超类型传参
 8 }
 9 
10 var desk=new Desk(200);
11 console.info(desk.name);  //["哥哥", "姐姐", "妹妹"]
12 console.info(desk.age);   //200
13 desk.name.push("JJ");
14 console.info(desk.name);  //["哥哥", "姐姐", "妹妹", "JJ"]
15 
16 var desk2 = new Desk(10);
17 console.info(desk2.name); ////["哥哥", "姐姐", "妹妹"]
 1 function Box(name, age) {
 2     this.name = name;
 3     this.age = age;
 4     this.family = ['哥哥','姐姐','妹妹'];    //引用类型,放在构造里就不会被共享 
 5 }
 6 
 7 function Desk(name, age) {
 8     Box.call(this, name, age)                //对象冒充,对象冒充只能继承构造里的信息
 9 }
10 
11 var desk = new Desk('Lee', 100);
12 console.info(desk.family);        // ["哥哥", "姐姐", "妹妹"] 
13 desk.family.push('弟弟');         // ["哥哥", "姐姐", "妹妹", "弟弟"]
14 console.info(desk.family);
15 
16 var desk2 = new Desk('Lee', 100);
17 console.info(desk2.family);       // ["哥哥", "姐姐", "妹妹"]


组合继承:

借用构造函数虽然解决了刚才两种问题,但没有原型,复用则无从谈起。所以,我们需要原型链+借用构造函数的模式,这种模式成为组合继承

 1 function Box(age){
 2     this.name=['Lee','Jack', 'Hello']
 3     this.age=age;
 4 }
 5 Box.prototype.run=function(){
 6     return this.name+this.age;
 7 };
 8 function Desk(age){
 9     Box.call(this, age); // 对象冒充
10 }
11 Desk.prototype=new Box(); //原型链继承
12 var desk=new Desk(100);
13 console.info(desk.run());

 



























 

转载于:https://www.cnblogs.com/molao-doing/articles/5479985.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值