博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
详细理解JS中的继承
阅读量:7287 次
发布时间:2019-06-30

本文共 5408 字,大约阅读时间需要 18 分钟。

正式说继承之前,有两个相关小点:
  1. JS只支持实现继承,即继承实际的方法,不支持接口继承(即继承方法的签名,但JS中函数没签名)
  2. 所有对象都继承了Object.prototype上的属性和方法。
  • 说继承之前还要再说一下原型。原型之所以很重要,原因之一就是可以利用它来实现JavaScript的继承。重写一个函数的原型对象,将其指定为另一个函数的实例,这样便实现了一个简单的原型链继承。
看一个利用原型链来实现继承的基础例子:
 
1 function Super(){ 2     this.name='super'; 3 } 4 Super.prototype.getName=function(){ 5     console.log(this.name); 6 } 7 function Sub(){ 8     this.name='sub'; 9 }10  11 Sub.prototype=new Super();//重写原型,实现继承12 var instance=new Sub();13 instance.getName();//sub继承了getName方法

重写原型会让Sub的原型获得Super构造函数上和Super原型上的所有属性和方法; 但是这样单纯使用原型来继承也有问题,比如将上面的代码修改一下,在原型上添加一个引用类型的属性:

1   2 function Super() { 3     this.name='super'; 4 } 5 Super.prototype.getName=function() { 6     console.log(this.name); 7 }; 8 Super.prototype.color = ["red", "black"]; 9 10 function Sub() {   11 }12 13 Sub.prototype=new Super();//实现继承14 15 var instance=new Sub();16 instance.color.push('white');//改变instance的color属性,push一个新的项17 var ins = new Sub();18 console.log(ins.color); //["red", "black", "white"]  可以看到ins的color属性也被改变了
可以看到在instance中修改color属性,但是在另一个实例ins中这个修改也被反映出来了,这就是一个问题。
  • ①  借用构造函数(又叫经典继承,伪造对象)
如果将属性和方法都写在原型上,利用其共享性来实现继承理论上没有问题,但是原型链的继承共享性固然好,可一个问题就出在其共享性上,对于引用类型的值,当在一个实例上修改了引用类型值之后,其他共享该引用类型的所有实例都随之改变了,如下:
1 function Arr(){2 }3 Arr.prototype.array=['red','black'];4 arr01=new Arr();5 arr02=new Arr();6 arr01.array.push('white');7 console.log(arr02.array);//["red", "black", "white"]
综上所以一般很少单独使用原型链,于是就有了借用构造函数这种思维,虽然这种做法也有一定的局限,如下: 
1 function superType(){ 2     this.color=['red','blue','yellow']; 3 } 4 function subType(){ 5     //继承了superType 6     superType.call(this); 7 } 8  9 var instance01=new subType();10 instance01.color.push('black');11 console.log(instance01.color);//['red','blue','yellow','black']12 13 var instance02=new subType();14 console.log(instance02.color);//['red','blue','yellow']

借用构造函数这种方法主要就是:利用parent . call(this)来继承父级构造函数上公有的属性,且在一个实例上进行修改不会对其他实例造成影响;[ 注意:使用call这种不能继承原型上属性和方法的哦 ]

当然,如果上面的例子中superType()有参数的话,在subType函数内部调用时也可以为其传递参数:superType . call(this,parameter);
 
  • ②  下面来说JS中最常用的继承模式:组合继承
1 function superType(name){ 2     this.name=name; 3     this.color=['red','blue','yellow']; 4 } 5 superType.prototype.sayName=function(){ 6     console.log(this.name); 7 } 8  9 function subType(name,age){10     //继承superType的属性11     superType.call(this,name);12     this.age=age;13 }14 //继承superType的方法15 subType.prototype=new superType();16 subType.prototype.constructor=subType;17 subType.prototype.sayAge=function(){18     console.log(this.age);19 };20 21 var instance01=new subType('lazy',20);22 instance01.color.push('black');23 console.log(instance01.color);//['red','blue','yellow','black']24 instance01.sayAge();//2025 instance01.sayName();//lazy26 27 var instance02=new subType('chen',21);28 console.log(instance02.color);//['red','blue','yellow']29 instance02.sayAge();//2130 instance02.sayName();//chen
 
这种方法就是既利用了call()来继承构造函数中公有的属性,同时又利用原型来继承原型对象上的属性和方法;这样让subType的实例既可以分别拥有各自独立的属性,也可以共用相同的方法了。
 
JS中用得做多的继承是组合式继承,而组合式继承最大的问题就是每次new一个subType的实例时,都会两次调用父级构造函数superType;
第一次是在重写subType的原型  : subType.prototype = new superType()  的时候。这一次调用让subType获得了superType构造函数上和原型对象上的属性和方法,且这些属性和方法是加在subType的原型对象中的;
第二次是在function subType(){
           superType.call(this,name);
      }
的时候,这一次在调用call方式时只获得了superType构造函数上的属性。注意这一次是在调用subtype构造函数创建subtype的实例,所以这次获得的属性方法是加在subType的实例中的,且这些实例中的属性其实会覆盖前面原型中同名的属性;
这样算起来每new一个subType的实例,就会两次调用superType。虽然这样两次下来就完全包含了superType的全部实例属性和方法,但执行了两次superType效率不算高。所以我们再来看另一个方法:
  • ③  寄生组合式继承
这种方法可以解决两次调用superType的情况,实际上 思路跟组合式继承是一样的;
1 function superType(name){ 2     this.name=name; 3     this.color=['red','blue','yellow']; 4 } 5 superType.prototype.sayName=function(){ 6     console.log(this.name); 7 } 8  9 function subType(name,age){10     //继承superType11     superType.call(this,name);12     this.age=age;13 }14 15 function inheritPrototype(sub,sup){16     //创建超类型构造函数的原型副本17     var prototype=Object(sup.prototype);18     //为其指定构造函数,增强对象19     prototype.constructor=sub;20     //重写sub的原型对象21     sub.prototype=prototype;22 }23 24 //copy一份超类型构造函数的原型对象给子类型构造函数25 inheritPrototype(subType,superType);26 27 subType.prototype.sayAge=function(){28     console.log(this.age);29 };30 31 var instance01=new subType('lazy',20);32 instance01.color.push('black');33 console.log(instance01.color);//['red','blue','yellow','black']34 instance01.sayAge();//2035 instance01.sayName();//lazy36 37 var instance02=new subType('chen',21);38 console.log(instance02.color);//['red','blue','yellow']39 instance02.sayAge();//2140 instance02.sayName();//chen

这种方法主要依然是利用   借用构造函数的方法来继承构造函数的属性,利用原型链的混合方法来继承方法;基本思路是:不必为了重写subType的原型而去调用一次superType,因为我们需要的也只是superType的原型对象的一个副本而已,所以有了inheritPrototype函数:

1 function inheritPrototype ( sub ,super) {2      var   prototype = Object( super.prototype );   //创建副本3      sub.prototype = prototype ;4      prototype . constructor = sub ;5 }
相比组合式继承这种方法高效率的地方就在于它只调用了一次superType构造函数;
  • ④寄生式继承 :在主要考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种很有用的模式,看个例子:
1 function createAnother(original) { 2     var clone = Object(original); 3     clone.sayHi = function () { 4         console.log('hi'); 5     }; 6     return clone; 7 } 8  9 var person = {10     name: 'lazy',11     friends:[1,2,3]12 };13 14 var anotherPerson = createAnother(person);15 anotherPerson.sayHi();16 console.log(anotherPerson.name);

这个例子中,基于person对象创建了一个新的对象anotherPerson,这个新的对象不仅具有person的所有属性和方法,而且还有自己的sayHi方法;

 

菜鸟小白一枚,可能上述有错误或理解不对的地方,恳请指出~~谢谢!

转载于:https://www.cnblogs.com/lazychen/p/5458044.html

你可能感兴趣的文章
空间地理信息产业:看好GIS“基础平台+应用开发”模式
查看>>
python 分割list
查看>>
Redhat 5.4搭建 DNS服务器解析负载
查看>>
m283屏幕花屏问题
查看>>
FreeBSD与Linux的比较
查看>>
redis配置文件全解及常用命令
查看>>
Zabbix汇总分组流量
查看>>
BootStrap之基础-1 BootStrap起步(基本概念、环境搭建)
查看>>
linux自学笔记--bash特性
查看>>
Linux平台中设置文件的执行、写权限
查看>>
CentOS7-虚拟网卡的删除
查看>>
Ruby中的include和extend
查看>>
Sencha的Eclipse插件提示和技巧
查看>>
超全前端面试题及答案
查看>>
使用纯真版IP地址库,根据IP判断所在地
查看>>
转:SQL注入攻击的原理
查看>>
DATA VISUALIZATION – PART 2
查看>>
如何用几何画板把圆奇数等分
查看>>
数据结构-线性表操作
查看>>
5Python全栈之路系列之算法
查看>>