在es6中已经有语法糖extends去实现类的继承,然而多继承是不被容许也是不被提倡的,由于会致使‘钻石问题’:好比说b和c都继承自a,d继承自b和c,那么d里面就会有同一个方法来自于两个两个祖先,那么当在d中调用这个方法时就会出现逻辑问题,究竟是调用b的呢仍是c的呢?java就是看到了c++多继承的问题因此就干脆不容许多继承了,js由于自身的灵活性,咱们依然能够经过一些方式来实现多继承(不少地方叫作mixin,实际上听起来更正确)。javascript
var A = function(name) { this.name1 = name; // this.name表明函数名,这里避免冲突 } var B = function(age) { this.age = age; this.getAge = function() {} } var C = function(name, age) { A.call(this, name); B.call(this, age); } // usage var c = new C('n', 2);复制代码
以上就是利用call或者apply来‘继承’父类属性及方法,其实本质上就是调用方法利用this把属性组合到一块儿。由于调用A方法是在上下文中加了一个name属性,调用B就是加了age属性,而你在new C的时候,js创造了一个对象,而且把上下文指向了这个对象,因此AB的属性也就天然添加到了这个对象里。html
因此下面用这个思路写一个extend方法:(extends是关键字,直接用这名字会报错)java
function extend(...args) { for (var i = 0; i < args.length; i++) { args[i].call(this) } } // usage var s = new extend(Class1, Class2);复制代码
固然你能够看到,用这个方法的话你是不能传参的c++
上面调用函数的方法模拟多继承当然能够知足咱们继承祖先的属性方法这一要求,但是这个最后的孩子其实不是真正的后代,由于不难看出他和前面的class不在同一条原型链上,因此用c instanceof A
返回false。并且一个缺点就是那个类的方法必须像this.getAge()这样定义在类里面,若是你new了多个实例就会重复创造这个方法,形成内存的浪费。es6
为了省内存,必然会想到往prototype添加方法的方式,然而经过上面的调用显然不能期望这些方法也出如今this上,因此咱们要经过new来复制这些方法算法
var A = function() { this.name1 = ''; } var B = function() { this.age = ''; } B.prototype.getAge = function() {} var extend = function(...args) { var ClassC = function() {} for (var i = 0; i < args.length; i++) { var base = new args[i](); for (var f in base) { ClassC.prototype[f] = base[f]; } } return ClassC; } // usage var C = extend(A, B); var c = new C();复制代码
for...in会把全部原型链上的可枚举属性都列出来,因此拿到那些类的实例后,再把这些属性拷贝到咱们本身类的原型上就行。bash
这个依然不能传参,不能instanceofmarkdown
function A(name) { this.name1 = name; } A.prototype.getName = function() {} function B(age) { weight = 100; // 私有属性,体重 this.age = age; } B.prototype.getAge = function() {} B.prototype.getWeight = function() {return weight;} B.prototype.addWeight = function() {weight++;} function extend() { for (var i = 0; i < arguments.length; i++) { var tempBase = new arguments[i](); for (var prop in tempBase) { if (!tempBase.hasOwnProperty(prop)) { // 只放链上的属性 this.prototype[prop] = tempBase[prop]; } } } } // usage function Child(name, age) { A.call(this, name); // 传参,以及添加自己的属性 B.call(this, age); } extend.call(Child, A, B); var child = new Child('lol', 5);复制代码
以上把前面代码改了两点,1是前面直接定义一个class在extend里面,因此直接写死了Class.protorype[prop] = ..., 这里写成this.prototype由于后面在调用extend方法时会把上下文指定成你的class,而且经过了hasOwnProperty去检查这个属性是否是本身的。app
2后面你就能够定义本身的构造器了,并实现的传参的须要。函数
这里稍微提一下私有属性,我看了一些博客后发现你们对私有属性理解好像有问题。其实像上面的weight直接写在里面就是私有的,由于私有的定义是这个function里面的其余函数能用之外,其余人都不行。好比私有属性/方法是没法继承的;这个类的实例也没法直接访问到的,要访问/修改必须你本身暴露出getter/setter,就好比上面的getWeight方法,若是你在Child类里面添加了weight的话就会返回它,不然返回父类的那个私有变量。
上面全部的都只是伪装继承,用instanceof都得不到你想要的结果不能用来判断某对象是否是某类的实例或子类,为了实现这一效果,你要写本身的isInstanceOf()方法,思路是在每一个子类里面存一个全部父类的引用而后作对比,另外为了解决多继承的钻石问题要用到MRO算法。下面博客写了这个,然而也不完美,因此各看官本身斟酌。
引用: