在学习js类的继承之前,首先查找学习了一些前置知识
一、js中定义类的写法:
function Person(name){
this.name = name;
}
Person.prototype.getName = function(){
return this.name;
}
实例化:var person = new Person("tom");
二、关于prototype:
每一个构造函数(如Person)都有一个prototype属性,这个属性就是对象实例(如person)的原型对象
调用某一方法时,若该方法在对象实例中不存在,就会去原型对象中查找,因此在原型对象中定义所有对象实例公用的属性与方法
prototype 对于构造函数来说,它是一个属性,对于对象实例来说,它是一个原型对象
三、原型链:
原型对象对于实例对象来说,它自身也是对象,也有自己的原型,形成了一条原型链。所有一切对象的顶端都是Object.prototype,
读取某个对象的属性时,会先寻找该对象自身的属性,若没有,则会去它的原型对象中寻找,原型中也没有,就去原型的原型中找,直至顶层的Object.prototype,若还是没有,就返回undefined
四、Constructor属性
prototype有一个属性constructor,默认指向prototype对象所在的构造函数
A.prototype.constructor === A
四、call方法
扩充函数运行的作用域,改变函数中this关键字的指向(指向传入的参数)
<script>
//例1
window.color = "red";
document.color="white";
function printColor(){
console.log(this.color);
}
printColor.call(window);//red
printColor.call(document);//white
//例2
var Animal = {
word : '...',
speak : function(){
console.log(this.word);
}
}
Animal.speak();//...
var Dog={ word : 'wang'}
Animal.speak.call(Dog);//wang
</script>
搞懂了前面这些知识点,便于我们更加容易理解和学习js的几种继承方式的写法
JS继承的实现方式
首先写一个父类
父类
function Father(name){
this.name = name || 'defaultName';
this.gender = [];
}
Father.prototype.eat = function(food){
console.log(this.name+":"+food);
}
1.原型链继承
function Son(){}
Son.prototype = new Father();
Son.prototype.name='Son';
var son1 = new Son();
var son2 = new Son();
son1.eat("rice");//Son:rice
son2.eat("noddle");//Son:noddle
son1.gender.push('male');
console.log(son1.gender);//["male"]
缺点:1.来自父类(原型对象)的引用属性被所有子类实例共享,在修改son1的gender属性,son2的gender属性随之改变
2.创建子类实例时,无法向父类构造函数传参
2.构造继承
function Son(){
Father.call(this);
}
var son = new Son();
son.eat("rice");//Uncaught TypeError: son.eat is not a function
console.log(son.name);//defaultName
console.log(son instanceof Father);//false
console.log(son instanceof Son);//true
创建子类实例时,可以向父类构造传参;子类实例不再共享父类的引用属性
缺点:1.子类无法继承原型属性和方法
2.创建的子类实例并不是父类的实例
3.组合继承
function Son(name){
Father.call(this);
this.name = name || 'sonName';
}
Son.prototype = new Father();
Son.prototype.constructor = Son;//修复构造函数指向
var son1 = new Son();
var son2 = new Son("Tom");
son1.eat('rice');//sonName:rice
son2.eat('rice');//Tom:rice
son1.gender.push('male');
console.log(son2.gender);//[]
console.log(son1 instanceof Son);//true
console.log(son1 instanceof Father);//true
解决了原型链继承和构造继承中存在的问题,只是调用了两次父类构造函数
4.实例继承
function Son(){
var instance = new Father;
instance.name = 'Son';
return instance;
}
var son = new Son();
console.log(son.name);//Son
console.log(son instanceof Son);//false
console.log(son instanceof Father);//true
重写父类的属性
缺点:创建的实例不是子类的实例
5.拷贝继承
function Son(name){
var father = new Father();
for(var p in father){
Son.prototype[p] = Father[p];
}
Son.prototype.name = name || 'sonName';
}
var son = new Son();
console.log(son.name);//sonName
console.log(son instanceof Father); // false
console.log(son instanceof Son); // true
遍历父类属性,逐一拷贝到子类中
6.寄生组合继承
function Son(name){
Father.call(this);
this.name = name || 'sonName';
}
(function(){
// 创建一个没有实例方法的类
var Super = function(){};
Super.prototype = Father.prototype;
//将实例作为子类的原型
Son.prototype = new Super();
})();
Son.prototype.constructor = Son;//修复构造函数指向
var son1 = new Son();
var son2 = new Son("Tom");
son1.eat('rice');//sonName:rice
son2.eat('rice');//Tom:rice
son1.gender.push('male');
console.log(son2.gender);//[]
console.log(son1 instanceof Son);//true
console.log(son1 instanceof Father);//true
解决了组合继承中存在的问题
参考:https://www.cnblogs.com/humin/p/4556820.html(JS实现继承的几种方式)
https://blog.csdn.net/ganyingxie123456/article/details/70855586(JS中的call()方法和apply()方法用法总结)
https://www.jb51.net/article/91826.htm(详解Javascript中prototype属性(推荐))