之前遇到过一道题:说什么是js的混合继承?无知的我以为是:对象自身属性和原型上方法的继承。妥妥的打脸啊[捂脸](写完这篇文章后,发现我这个说法好像也是没啥毛病啊,混合继承就是用对象冒充来继承另外一个构造函数的自身属性,改写对象prototype指向另外一个对象,就是对象自身属性和原型的继承嘛[捂脸])
那么首先来说说继承机制的实现方式吧:
首先是基类的创建,再就是子类的创建,创建的子类将继承超类的所有属性和方法,包括构造函数以及方法的实现。所有属性和方法都是公用的,因此子类可以直接访问这些方法。子类还可以添加超类中没有的新属性和方法,也可以覆盖超类的属性和方法。
javascript中继承的方式:
1.对象冒充
原理:构造函数使用 this 关键字给所有属性和方法赋值,所以可使ClassA 构造函数成为ClassB的方法,然后调用它,ClassB就会收到ClassA中定义的属性和方法。例如:
function ClassA(sColor){ this.color = sColor; this.sayColor = function(){ alert(this.color); } } function ClassB(sColor,sName){ this.newMethod = ClassA; this.newMethod(sColor); delete this.newMethod; this.sayName = function(){ alert(sName); } } var objA = new ClassA("blue"); var objB = new ClassB("red","miya"); objA.sayColor(); //blue objB.sayColor(); //red objB.sayName(); //miya
ClassA赋予了方法newMethod(函数名只是指向它的指针),然后调用该方法,传递给的是ClassB的参数sColor,所有新属性和新方法都必须在删除了新方法的代码行后定义,否则可能会覆盖超类的相关属性和方法。
对象冒充可以实现多重继承
一个类可以继承多个超类。如果存在两个类ClassX和ClassY具有同名的属性或者方法,下面的ClassY具有更高的优先级。所以多个对象继承时候注意下属性方法的覆盖。
在javascript中call()和apply()就是对象冒充最为相似的方法。
function sayColor(f){ console.log(`${f} ${this.color}`); } var obj = new Object(); obj.color = 'green'; sayColor.call(obj,'I like') //I like green
sayColor()里面的this关键字指向obj,对象冒充和继承机制一起使用,如下:
function ClassA(sColor){ this.sayColor = function(){ alert(sColor); } } function ClassB(sColor,sName){ ClassA.call(this,sColor); this.name = sName; this.sayName = function(){ alert(this.name); } } var c = new ClassB('green','miya'); c.sayColor() //green c.sayName() //miya
ClassB继承了ClassA的方法。apply和call作用相同,只是传参形式不同,apply传递的是数组形式的。
2.原型链的继承
prototype是个模板,要实例化的对象都以这个模板为基础,prototype对象的任何属性和方法都被传递给那个类的所有实例。
ClassB.prototype = new ClassA();
将ClassB的原型指向了一个ClassA的实例对象上,所以,ClassB的 原型就是ClassA的实例对象。
var objB = new ClassB(); objB instanceof ClassA //true objB instanceof ClassB //true
原型链的弊端:不支持多重继承,会重写类的prototype属性。
3,混合方式继承
终于说到话题最开始的时候了,什么是混合继承?
使用构造函数定义类,并非使用任何原型。对象冒充的主要问题是必须使用构造函数方式,如果使用原型链就无法使用带参数的构造函数,对象冒充和原型链继承方式都用,就是混合继承。
创建类最好的方式:用构造函数定义属性,用原型定义方法。
混合继承就是:用对象冒充继承构造函数的属性,用原型链继承prototype对象的方法。
function ClassA(scolor){ this.color = scolor; } ClassA.prototype.sayColor = function(){ console.log(this.color); } function ClassB(scolor,sname){ ClassA.call(this,scolor); this.name = sname; } ClassB.prototype = new ClassA(); ClassB.prototype.sayName = function(){ console.log(this.name); } var objA = new ClassA("blue"); var objB = new ClassB("red","miya"); objA.sayColor(); //blue objB.sayColor(); //red objB.sayName(); //miya
objA instanceof ClassA //true
objB instanceof ClassA //true
objB instanceof ClassB //true
在该例子中,继承的突出在于,ClassB构造函数中,用对象冒充继承了ClassA的自身属性,将ClassB的原型对象指向了ClassA的实例对象上面,然后再在原型上面添加新的方法。
因为使用了原型链,所以可以使用instanceof进行测试:对象是否继承某个类的原型