Js的继承是基于原型实现的
常见的继承7种方式:
- 原型继承
- 借用构造函数继承(经典继承)
- 原型链+构造函数的组合继承
- 原型式继承
- 寄生式继承
- 寄生组合式继承
- ES6 Class继承
原型继承
这种方式是把子类原型指向父类实例,子类实例通过原型链找到父类实例,再通过父类实例找到父类原型,实现继承.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function Sup(name) { this.name = name this.type = 'human' this.ob = { a:1, b:2 } } Sup.prototype.getName = function () { console.log(this.name) } Sup.prototype.obj = {a:1,b:2} function Sub(age) { this.age = age } Sub.prototype = new Sup('gss') Sub.prototype.getAge = function () { console.log(this.age) }
|
优点:
- 父类/父类原型更新属性和方法时,子类都能访问到
- 简单,易实现
缺点:
- 无法实现多继承(继承多个父类)
- 原型对象的引用属性被多个实例共享,不管是私有还是公有属性
- 创建子类实例不能向父类传参
- 给子类添加原型属性或方法都必须在实例父类之后操作
原型式继承
通过Object.create(),将第一个参数作为原型创建对象
1 2 3 4
| function Sup () {} Sup.prototype.sayHi = function () { console.log('hi~') }
let s = Object.create(Sup)
|
优缺点同上
借用构造函数继承(经典继承)
这种方法关键在于:在子类构造函数中通过call()调用父类构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function Sup (name) { this.name = name this.obj = { a:1, b:2 } } Sup.prototype.getName = function () { console.log(this.name); } function Sub (name, age) { this.age = age Sup.call(this, name) } let g = new Sub('gss', 18) let gg = new Sub('ggss', 180) g.obj.a = 10 console.log(g, gg); g.getName()
|
优点:
- 解决引用数据共享问题,每个子类实例都有单独的实例数据
- 能向父类传参
- 能多继承
缺点:
- 不能继承原型属性,方法
- 每次创建子类都调用父类方法,性能影响
- 实例不是父类的实例,只能是子类的实例
原型链+构造函数的组合继承
这种方法关键在于:在子类构造函数中通过call()调用父类构造函数;再将父类实例作为子类原型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| function Sup (name) { this.name = name this.obj = { a: 1, b: 2 } } Sup.prototype.getName = function () { console.log(this.name) }
function Sub (name, age) { this.age = age Sup.call(this, name) } Sub.prototype = new Sup() Sub.prototype.constructor = Sub
let s = new Sub('gss', 18) let ss = new Sub('victor', 20) ss.obj.a = 10 console.log(s, ss) s.getName()
|
优点:
- 解决引用数据共享问题,每个子类实例都有单独的实例数据
- 能向父类传参
- 能多继承
- 可以继承父类实例和原型方法,属性
缺点:
寄生式继承
关键在于:创建一个仅用于封装继承过程的函数,该函数以某种方式在内部加强对象,并返回对象(相当于原型式继承封装)
1 2 3 4 5 6 7 8 9 10
| function createObj (obj) { let result = Object.create(obj) result.name = 'gss' return result } let obj = { age: 1, sex: 0 } let o = createObj(obj)
|
寄生组合式继承
类似组合继承(原型链+构造函数),也是把继承过程封装在一个函数内
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| function inheritPrototype (sup, sub) { let protoType = Object.create(sup.prototype) protoType.constructor = sub sub.prototype = protoType } function Sup (name) { this.name = name this.obj = { a: 1, b: 2 } } Sup.prototype.getName = function () { console.log(this.name) }
function Sub (name, age) { Sup.call(this, name) this.age = age } inheritPrototype(Sup, Sub)
let s = new Sub('gss', 18) let ss = new Sub('victor', 20) ss.obj.a = 10 console.log(s, ss)
|
ES6 Class继承
一. class
- ES6中class可以看作是语法糖,绝大部分功能es5都能做到;
- 其内部默认严格模式;
- constructor是类的默认方法,必须有,不写会隐式添加
- 不存在变量提升
- 本质是function
- 不提供私有方法和属性,提案方法属性前加#标识私有
- 用static标识静态方法,没有静态属性,须在类外部添加静态属性
- name属性返回类名,表达式形式等号后的名(.name)只能在类的内部使用,正常调用用等号前的名
- 内部可以用get,set取存值
- 静态方法可以被子类继承,可用super调用
- new.target属性用于确定构造函数是怎么调用的,普通函数直接调用为undefined,通过new调用指向函数本身,类中指向被new的类(类必须new,普通调用报错)
- 命名首字大写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| let MySup = class Sup { constructor (name) { console.log(new.target) this.name = name this._age = 18 } static getClass () { console.log(Sup.name) console.log(this) } getName () { console.log(this.name) } get age () { return this._age } set age (val) { this._age = val } } console.log(typeof MySup) let s = new MySup('gss') s.getName() MySup.getClass() console.log(MySup.name) console.log(s.age) s.age = 20 console.log(s.age) MySup.sex = 0 console.log(MySup.sex)
class Sub extends MySup {}
console.log(Sub.sex) MySup.getClass() new Sub()
|
二. class的继承
- 继承通过extends关键字
- super既能当函数用,也能当对象用
- 作为函数时表示父类构造函数
- 虽然是父类构造函数,但返回的实例是子类,即super内部改变this指向
- 作为对象时,在普通方法中指父类原型对象,在静态方法中只父类
- 通过super赋值时相当于this,是子类的实例属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Sub extends Sup { constructor (a,b,c) { super(a,b) super.c = c } static showStatic () { super.show() } showInstance () { super.show() } } let s = new Sub(1,2) console.log(s.show()); console.log(Sub.show()); console.log(s.showInstance()); console.log(Sub.showStatic());
|
- class作为语法糖,同时有prototype和proto,因此同时存在两条继承链
- 子类__proto__表示构造函数的继承,指向父类
- 子类的prototype的__proto__表示方法的继承,指向父类的prototype
1 2 3 4 5 6 7 8 9 10 11
| class Sup {} class Sub extends Sup{} Sub.__proto__ === Sup Sub.prototype.__proto__ === Sup.prototype
Sup.__protp__ === Function.prototype Sup.prototype.__proto__ === Object.prototype
class A extends null {} A.__proto__ === Function.prototype A.prototype.__proto__ === undefined
|