js本身是基于面向对象开发的编程语言。类有以下特性:封装、继承、多态。那这几种特性在js中是如何表现的呢?
封装:js中类也是一个函数,把实现一个功能的代码进行封装,以此实现低耦合高内聚
多态:包括重载和重写
- 重载:相同的方法,由于参数或者返回值不同,具备不同的功能。js中不具备严格意义上的重载,因为在其它语言中,不同的传参可以分为不同的函数。如果想要js实现重载,就只能是在同一个方法内,根据传参不同实现不同的功能
- 重写:子类重写父类上的方法(伴随着继承)
继承:子类继承父类上的方法
继承
在js语言中,他的继承和其他语言不太一样
继承的目的:让子类的实例同事也具备父类中私有的属性和公共方法
function Parent(){
this.x = 100
}
Parent.prototype.getX = function getX(){
return this.x
}
function Child(){
this.y = 200
}
Child.prototype.getY = function getY(){
return this.y
}
let c1 = new Child
console.log(c1)
复制代码
想让 c1
也可以有 x
和 getX
继承的方法
原型继承
让子类的原型等于父类的实例即可
因为子类的实例,能够用子类私有的和原形公有的,同样,父类的实例也能够用父类私有的和原形公有的。
function Parent(){
this.x = 100
}
Parent.prototype.getX = function getX(){
return this.x
}
function Child(){
this.y = 200
}
Child.prototype = new Parent//加一句话即可
Child.prototype.constructor = Child//3指定construct
Child.prototype.getY = function getY(){
return this.y
}
let c1 = new Child
console.log(c1)
复制代码
原型链的指向如下:
原型继承的特点
- 把父类中私有的和公有的属性方法,继承完后,最终都变为子类实例公有的
- 和其他语言不通,原型继承并不会吧父类的属性方法拷贝给子类,而是让子类实例基于
__proto__
原型链找到自己自定义的属性和方法(”指向/查找”方式的继承,而不是拷贝式继承)
重写:
c1.__proto__.xxx=xxx
修改子类原型(原有父类的一个实例)中的内容,内容被修改后,对子类的其他实例有影响,但是对父类的实例不会有影响
c1.__proto__.__proto__.xxx=xxx
直接修改的是父类的原型,这样不仅会影响其他父类的实例,也影响其他子类的实例
call
继承
在子类构造函数中,把父类当做普通方法执行,并且将父类中的this指向子类。
function Parent(){
this.x = 100
}
Parent.prototype.getX = function getX(){
return this.x
}
function Child(){
//在子类构造函数中,把父类当做普通方法执行(没有父类实例,父类原型上的那些东西也就和他没关系了)
//this.x = 100相当于强制给c1这个实例设置一个私有的属性x,属性值100,相当于让子类的实例继承了父类的私有属性,并且也变为了子类私有的属性
Parent.call(this)
this.y = 200
}
Child.prototype.getY = function getY(){
return this.y
}
let c1 = new Child
console.log(c1.getX())
console.log(c1)
复制代码
这种方法无法继承父类的私有属性。
上面两种继承都有缺点,我们想要的是父类私有变成子类私有,父类共有也变为子类私有
寄生组合继承
如果想让子类也继承父类的公有属性,只要保证 Child.prototype.__proto__
指向 Parent.prototype
即可
分为两步
Parent.call(this)
继承私有属性和方法Child.prototype.__proto__ = Parent.prototype
继承公有属性和方法
其实就是call继承+另类原型继承的组合
function Parent(){
this.x = 100
}
Parent.prototype.getX = function getX(){
return this.x
}
function Child(){
Parent.call(this)//1.
this.y = 200
}
Child.prototype.__proto__ = Parent.prototype//2.
Child.prototype.constructor = Child//3
Child.prototype.getY = function getY(){
return this.y
}
let c1 = new Child
console.log(c1.getX())
console.log(c1)
复制代码
如果浏览器不支持 __proto__
(例如ie),那么我们可以借助 Object.create
Object.create()
方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
即创建一个新对象,并且将 __protot__
指向传入的对象
所以将 Child.prototype.__proto__ = Parent.prototype
修改为 Child.prototype = Object.create(Parent.prototype)
最后再指定 constructor
即可
function Parent(){
this.x = 100
}
Parent.prototype.getX = function getX(){
return this.x
}
function Child(){
Parent.call(this)//1.
this.y = 200
}
Child.prototype.__proto__ = Parent.prototype//2
Child.prototype.constructor = Child//3
Child.prototype.getY = function getY(){
return this.y
}
let c1 = new Child
console.log(c1.getX())
console.log(c1)
复制代码
ES6继承
使用 extends
关键字。
注意:继承后一定要在 constructor
第一行加上 super()
class Parent{
constructor() {
this.x = 100
}
getX(){//相当于在原型上写方法
return this.x
}
}
//继承:extends Parent
//注意:继承后一定要在constructor 第一行加上super()
class Child extends Parent{
constructor() {
super()
this.y = 200
}
getY(){
return this.y
}
}
let c1 = new Child
console.log(c1.getX())
console.log(c1)
复制代码
不加 super()
会报错
'Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor'
执行 super()
类似于我们之前的call继承,如果执行 super(100,200)
,就相当于把前面的 Parent
构造函数执行,并传入 100,200
最终实现效果类似于寄生组合继承