js继承的方式总结
- 对象冒充
- call()函数
- apply()函数
- 原型链
- 混合方式
对象冒充
function Father(name,age,sex){
this.name=name;
this.age=age;
this.sex=sex;
this.say=function(){
alert('hello world');
}
}
function Son(name,age,sex){
this.extend=Father;//将构造方法当成普通方法使用,赋值给属性
this.extend(name,age,sex);//调用
delete this.extend;//删除对Father的引用
//所有新属性和新方法都必须在删除了新方法的代码行后定义。否则,可能会覆盖超类的相关属性和方法,因为this.extend创建了对Father的引用
}
var son=new Son('son',18,'男');
var father=new Father('father',38,'男');
console.log(son.name);//son
console.log(father.name);//father
实现原理:让父类的构造函数成为子类的方法,然后调用该子类的方法,通过this关键字给所有的属性和方法赋值。
该种实现方式可以实现多继承。
这里存在一个弊端,如果存在两个类 ClassA 和 ClassB 具有同名的属性或方法,ClassB 具有高优先级。因为它从后面的类继承。除这点小问题之外,用对象冒充实现多重继承机制轻而易举。
call()函数
function Father(name,age,sex){
this.name=name;
this.age=age;
this.sex=sex;
this.say=function(){
alert('hello world');
}
}
function Son(name,age,sex,grade){
Father.call(this,name,age,sex);
this.grade=grade;
}
var son=new Son('key',18,'男',100);
console.log(son.name);//key
console.log(son.grade);//100
实现原理:改变函数内部的函数上下文this,使它指向传入函数的具体对象。
该种方式不能继承原型链,若想继承原型链,则采用第5种混合模式。
apply()函数
function Father(name,age,sex){
this.name=name;
this.age=age;
this.sex=sex;
this.say=function(){
alert('hello world');
}
}
function Son(name,age,sex,grade){
Father.apply(this,[name,age,sex]);//参数以数组的形式
this.grade=grade;
}
var son=new Son('key',18,'男',100);
console.log(son.name);//key
console.log(son.grade);//100
实现原理:改变函数内部的函数上下文this,使它指向传入函数的具体对象。
该种方式不能继承原型链,若想继承原型链,则采用5混合模式,与call()函数的区别就是参数是以数组的形式传人。
原型链
function Father(name,age,sex){
this.name=name;
this.age=age;
this.sex=sex;
this.say=function(){
alert(this.name);
}
}
function Son(grade){
this.grade=grade;
}
/*调用 Father 的构造函数,没有给它传递参数。这在原型链中是标准做法。要确保构造函数没有任何参数。
与对象冒充相似,子类的所有属性和方法都必须出现在 prototype 属性被赋值后,因为在它之前赋值的所有方法都会被删除。为什么?因为 prototype 属性被替换成了新对象,添加了新方法的原始对象将被销毁。*/
Son.prototype=new Father();//直接将实例赋值给子类原型
var son=new Son(100);
son.name="key";
console.log(son.name);//key
son.say();//key
实现原理:使子类原型对象指向父类的实例以实现继承,即重写类的原型,弊端是不能直接实现多继承。
混合方式
function Father(name) {
this.name = name;
}
Father.prototype.say = function () {
alert(this.name);
};
function Son(name, age) {
Father.call(this, name);
this.age = age;
}
Son.prototype = new Father();
Son.prototype.sayAge = function () {
alert(this.age);
};
var father = new Father("father");
var son = new Son("son", 18);
father.say();//father
son.say();//son
son.sayAge();//18
以下是w3cSchool中的原话,我觉得写的很好!
对象冒充的主要问题是必须使用构造函数方式,这不是最好的选择。不过如果使用原型链,就无法使用带参数的构造函数了。开发者如何选择呢?答案很简单,两者都用。
我们曾经讲解过创建类的最好方式是用构造函数定义属性,用原型定义方法。这种方式同样适用于继承机制,用对象冒充继承构造函数的属性,用原型链继承 prototype 对象的方法。
目录
浏览器兼容
- 所有主流浏览器都兼容。