es5中函数的继承
在es5中实现继承的方法有:
1、原型链继承
2、借助构造函数继承
3、组合式继承
4、原型式继承
5、寄生式继承
6、寄生式组合继承
1、原型链继承:顾名思义就是依靠原型链实现继承,关键点在于让父类构造函数创建的实例赋值给子类的原型,这样子类的实例通过原型链可以访问到父类的原型(可以使用父类原型上的方法),除此之外因为父类的实例赋值给了子类的原型,所以父类构造函数中的this也就是子类原型,子类的原型上就有了父类构造函数的实例属性,进一步子类的实例可以通过原型链访问到父类构造函数的实例属性。
function Parent(name){
this.name = name;
this.age = 18
}
Parent.prototype.sayhi = function(){
return 'hi'
}
function Son(){}
Son.prototype = new Parent('sari')
let son1 = new Son();
let son2 = new Son();
console.log(son1.name)
console.log(son2.name)
console.log(son1.sayhi())
console.log(son2.sayhi())
缺点:虽然通过这种方法,子类的实例不仅可以访问到父类原型上的方法,还可以访问到父类的实例属性。但是无法在不改变所有子类实例的情况下给父类构造函数传递参数。
2、借助构造函数继承:这个方法其实是改变父类的this指向,让他的this指向子类的实例,那么子类实例就能够使用父类的实例属性。这样每次创建子类实例的时候都会相应的创建一次父类的构造函数,因为有下面的Parent.call(this,args)。就解决了方法1中的缺点。
function Parent(name){
this.name = name;
this.age = 18
}
function Son(args){
Parent.call(this,args)
}
let son1 = new Son('hello')
console.log(son1.name)
3、组合式继承:这个方法是取了1、2方法的优点,用原型链继承的方式实现对父类原型方法的继承,利用借助构造函数的方法实现对实例属性的继承
function Parent(name){
this.name = name;
this.age = 18;
}
Parent.prototype.sayhi= function(){
return 'hi'
}
function Son(args){
Parent.call(this,args)
};
Son.prototype = new Parent();
let son1 = new Son('tom')
let son2 = new Son('xiaoming')
console.log(son1.name)
console.log(son2.name)
console.log(son1.sayhi())
console.log(son2.sayhi())
//tom xiaoming hi hi
4、原型式继承:原型原型,说白了也是用到了原型链的东西,其核心就是在封装的函数中的 F.prototype = o;这个封装的object函数,返回的是F的实例,让这个实例的构造函数的原型指向参数o,这样就可以继承o的属性
let obj = {
name:'tom',
age:18
}
function object(o){
let F = function(){}
F.prototype = o
return new F
}
let o1 = object(obj)
console.log(o1.name)
console.log(o1.age)
// tom 18
后来这个方法演化成了Object.create(o)的方法,就是创建一个实例对象,这个实例对象 instanceof o;也就是生成的实例可以通过原型链访问到o的属性。
let obj = {
name:'tom',
age:18
}
let p = Object.create(obj);
console.log(p.name);
console.log(p.age)
// tom 18
5、寄生式继承:寄生式继承就是在原型式继承的基础上进行相应的添加,如下:可以添加一个方法等。
let obj = {
name:'tom',
age:18
}
function object(o){
let F = function(){}
F.prototype = o
return new F
}
function createPerson(o){
let p = object(o)
p.sayhi = function(){
return 'hi'
}
return p
}
let p1 = new createPerson(obj)
console.log(p1.name)
console.log(p1.sayhi())
// tom hi
6、寄生式组合继承:虽然组合式继承已经算是比较完美,但是还是有一定的缺点,比如,每次继承都会调用两次父类构造函数,在Son.prototype = new Parent()和Parent.call(this,args)时调用.所以需要使用寄生式组合继承。他的真正的核心是: let prototype = object(Parent.prototype),其他部分跟组合式继承没什么区别。
function object(o){
let F = function(){}
F.prototype = o
return new F
}
function Parent(name){
this.name = name;
this.age = 18
}
Parent.prototype.sayhi=function(){
return 'hi'
}
function Son(args){
Parent.call(this,args)
}
let prototype = object(Parent.prototype)
Son.prototype = prototype
let son1 = new Son('tom')
console.log(son1.name)
console.log(son1.sayhi())
// tom hi
es6的class和extends继承
class类其实是一个语法糖,他的本质还是一个函数
class Person{}
console.log(typeof(Person))
//function
class分成三个部分,构造器函数部分,原型方法部分,静态方法部分
class Person{
constructor(name,age){
this.name = name;
this.age = age
}
sayhi(){
return 'hi'
}
static eat(){
return 'eat somthing'
}
}
let p = new Person('tom',19)
console.log(p.name)
console.log(p.age)
console.log(p.sayhi())
// tom 19 hi
从上面可以看出构造器函数对应着实例属性,原型方法部分(sayhi)是定义在原型上的方法,静态方法(static eat)是属于类自身的方法。 通过extends实现继承
class Parent{
constructor(name,age){
this.name = name;
this.age = age
}
sayhi(){
return 'hi'
}
static eat(){
return 'eat somthing'
}
}
class Son extends Parent{}
let son1 = new Son('tom')
console.log(Son.eat())
console.log(son1.name)
console.log(son1.sayhi())
// eat somthing; tom; hi
可以看出子类完美继承了父类的实例属性,原型方法,静态方法 继承原型方法的原理是:Son.prototype.proto = Parent.prototype
继承静态方法的原理是:Son.proto = Parent
继承实例属性则依靠super(),super()会调用父类的构造函数,将返回的实例指向this。super()调用构造函数时如果需要传参,则手动传入。super(args)其实相当于Parent.constructor.call(this,args)