以下只列举三种常见方式
1:拷贝继承
首先我们要知道子类究竟要继承父类的哪些特征?
答案是属性和方法。继承父类的属性。我们采用call通过对象冒充的方式。让子类具有父类的属性。那么接下来我们所说的三种方式,都是针对父类的方法。更直接一点,就是父类prototype上的方法。
那么我们就好理解。所谓拷贝继承。就是通过拷贝的方式,把父类prototype上的方法统统赋值给 子类的prototype
接下来我们看一个简单的例子。
function Person(name,age){
this.name=name;
this.age=age;
}
Person.prototype.eat=function(){
console.log("eat")
}
function Student(name,age,className){
Person.call(this,age,name)
this.className=className
}
for(var attr in Person.prototype){
Student.prototype[attr]=Person.prototype[attr]
}
var s1=new Student();
console.log(s1.constructor)
s1.eat(); --》eat
2 类式继承
所谓类式继承。也就是通过一个父类新建一个实例类,由于该实例是由父类new出来的,所以就具有了所有父类的属性和方法。接下来我们只需要把子类的prototype指向这个实例类。那么子类的原型上就具有了父类的方法。
function Person(name,age){
this.name=name;
this.age=age
}
Person.prototype.eat=function(){
console.log("eat")
}
var p=new Person(); //所谓类式继承,就是通过父类创建一个类的实例,把子类的原型链等于这个类的实例,实现继承
function Student(name,age,className){
Person.call(this,name,age)
this.className="two"
}
Student.prototype=p;
var s1=new Student("sean",20);
s1.eat() --》eat
这个有一个问题:Student.prototype=p;本质是对象的引用。所以当我们修改Student.prototype.eat里函数的内容时,父类的方法也发生了变化。这时候我们只需要通过Student.constructor=Student。修正它的构造函数指向,这样就可以避免这个问题。constructor 属性是专门为 function 而设计的,它存在于每一个 function 的prototype 属性中。这个 constructor 保存了指向 function 的一个引用。也就是说constructor 的值实际上是其构造函数。
3 原型继承
所谓原型继承:其实和类式继承很相识。只不过类式继承是通过把一个父对象的实例等于子类的原型prototype属性来实现,而
原型继承则是通过一个空函数来实现,也就是把父类的原型赋值给空函数,再把该空函数返回的一个实例对象等于子类的原型prototype属性。有点绕。
基本上分3步骤:
(1)var F = function(){};
(2)F.prototype = Parent.prototype;
(3)Child.prototype = new F();
还是通过上面的例子:
function Person(name,age){
this.name=name;
this.age=age
}
Person.prototype.eat=function(){
console.log("eat")
}
function Student(name,age,className){
Person.call(this,name,age)
this.className="two"
}
function fn(){}
fn.prototype=Person.prototype;
Student.prototype=new fn();
var s1=new Student("sean",20);
s1.eat()
原型继承还是要相比类式继承的优点在于通过空函数占用内存较小。但是它还是存在子类constructor 指向的构造函数出错的问题。所以还是要手动修正。加上Student.constructor =constructor
function Person(name,age){
this.name=name;
this.age=age
}
Person.prototype.eat=function(){
console.log("eat")
}
function Student(name,age,className){
Person.call(this,name,age)
this.className="two"
}
function fn(){}
fn.prototype=Person.prototype;
Student.prototype=new fn();
Student.constructor =Student
var s1=new Student("sean",20);
s1.eat()
console.log(Student.constructor) --->function Student(){ ..... }
es6中定义了类的概念。不需要我们每次去写一个founction FN()这样一个构造函数。而通过class关键字去申明一个类。写法简洁。但是这只是一个语法糖,写法简单了,内部还是采用es5 构造函数去定义的。es5中我们的属性都通过传参到构造函数中,es6中则把属性通过参数的形式写在了一个叫constructor的构造函数中。方法在es5中通过prototype上定义函数的形式来完成。而es6中则直接简写成类似这样的形式
eat(){
console.log("eat")
}
//定义一个person类
class Person{
constructor(name,age){
this.name=name;
this.age=age
}
eat(){
console.log("eat")
}
}
注意:
每一个类都会有一个默认的固定的方法,这个方法名字 constructor
constructor构造函数:对类进行初始化的,当我们通过new的方式调用该类的时候,默认执行的函数就是这个 constructor 函数,所以 constructor 其实就是通过类创建对象的时候的初始化函数,我们会通过这个函数对产生的对象进行一些初始化的工作,比如属性初始化
注意:class中的方法,其实就是一个函数,但是函数不能有function
同样上面的例子:
class Person{
constructor(name,age){
this.name=name;
this.age=age
}
eat(){
console.log("eat")
}
}
class Student extends Person{
constructor(name,age,className){
super(name,age) //super关键词用来继承父类的属性,相当于在es5中call的作用
this.className=className //子类新的属性
}
test(){
console.log("考试")
}
}
var p=new Person()
var s=new Student("sean","24")
console.log(s.name) ----》sean
ES6中类的出现。简化了写法。让我们不必再去更多的考虑写法。而是关注内容本身