ES5实现类的继承
实质是先创造子类的实例对象this
,然后再将父类的方法添加到this
上面,Parent.apply(this)
。
function Phone(brand, price) {
this.brand = brand
this.price = price
}
Phone.prototype.makePhone = function () {
console.log('我可以打电话!')
}
function SmartPhone(brand, price, color) {
Phone.call(this, brand, price)
this.color = color
}
SmartPhone.prototype = new Phone
SmartPhone.prototype.constructor = SmartPhone
var sp = new SmartPhone('华为', 4999, '黑色')
console.log(sp.brand) // '华为'
console.log(sp.price) // 4999
console.log(sp.color) // 黑色
sp.makePhone() // 我可以打电话!
sp instanceof Phone // true
sp instanceof SmartPhone // true
Class类实现继承
Class
可以通过extends
关键字实现继承,比ES5
实现继承的方式要清晰和方便很多。
实质是先将父类实例对象的属性和方法,加到this
上面(所以必须先调用super
方法),然后再用子类的构造函数修改this
。
constructor和super函数
constructor
中调用super
以后,相当于深拷贝了一份this
对象,子类修改成员变量也会影响super
的调用结果
class Phone {
constructor(brand, price) {
this.brand = brand
this.price = price
}
makePhone () {
console.log('我可以打电话')
}
printBrand () {
console.log(`手机品牌:${this.brand}`)
}
}
class SmartPhone extends Phone {
constructor(brand, price, color) {
super(brand, price)
this.color = color
}
makePhone () {
console.log('---升级前---')
super.makePhone()
console.log('---升级后---')
console.log('我可以打视频电话')
}
changeBrand (brand) {
super.printBrand()
this.brand = brand
super.printBrand()
}
}
var sp = new SmartPhone('小米', 2199, '黑色')
console.log(sp) // {brand: "小米", color: "黑色", price: 2199}
sp.changeBrand('华为')
// 手机品牌:小米
// 手机品牌:华为
sp.makePhone()
// ---升级前---
// 我可以打电
// ---升级后---
// 我可以打视频电话
sp instanceof Phone // true
sp instanceof SmartPhone // true
- 子类必须在
constructor
方法中调用super
方法,通过父类得到this
对象,而且this
只能在super
调用后使用
class Phone {
constructor() {}
}
// 必须在constructor中调用super函数,否则会报错
class ESPhone extends Phone {
constructor() {
// Must call super constructor in derived class before accessing 'this' or returning from derived constructor
}
}
var esp = new ESPhone('小米', 2199, '黑色')
- 如果子类中没有显示定义
constructor
函数,会默认添加,并将所有的参数通过rest
操作符传入
class Phone {
constructor(brand, price) {
this.brand = brand
this.price = price
}
makePhone () {
console.log('我可以打电话')
}
}
class SmartPhone extends Phone {}
// 等同于
// class SmartPhone extends Phone {
// constructor (...argus) {
// super(...argus)
// }
// }
var sp = new SmartPhone('小米', 2199, '黑色') // success
Class类静态方法的继承
静态方法只能继承静态方法 而且必须通过super()
来调用 此时super
指向构造函数本身
class Phone {
static systemName = '安卓'
static version = '12.0.1'
static aboutPhone() {
console.log(`系统:${this.systemName};版本:${this.version}`)
}
}
class SmartPhone extends Phone {
static printAbout() {
super.aboutPhone()
}
}
SmartPhone.printAbout() // 系统:安卓;版本:12.0.1
Object.getPrototypeOf()
可以使用这个方法判断,一个类是否继承了另一个类
class Phone {}
class SmartPhone extends Phone {}
var parent = Object.getPrototypeOf(SmartPhone)
console.log(parent === Phone) // true
super关键字
super函数调用
super
作为函数调用的时候,代表父类的构造函数,ES6
规定了,子类的构造函数必须执行一次super
函数;
必须显式指定是作为函数、还是作为对象使用,否则会报错;
由于对象总是继承其他对象的,所以可以在任意一个对象中,使用super
关键字;
super()
函数内部this
指向的是子类
class Phone {
constructor(brand, price) {
this.brand = brand
this.price = price
console.log(new.target.name)
}
}
class SmartPhone extends Phone {
constructor(brand, price, color) {
super(brand, name)
// 相当于 Phone.prototype.constructor.call(this, brand, price)
this.color = color
}
}
var sp = new SmartPhone('华为', 4999, 'black') // SmartPhone
- 只能在子类的构造函数中调用
super
函数,否则会报错
class Phone {}
class SmartPhone extends Phone {
makePhone() {
super() // Uncaught SyntaxError: 'super' keyword unexpected here
}
}
Super对象使用
super
作为对象使用的时候,在普通方法中,指向父类的原型对象,在静态方法中使用,指向父类本身;
class Phone {
makePhone() {
console.log('我可以打电话!')
}
static aboutPhone() {
console.log('关于本机')
}
}
class SmartPhone extends Phone{
constructor() {
super()
super.makePhone()
// console.log(super) // error 报错
}
static aboutPhone() {
super.aboutPhone()
}
}
var sp = new SmartPhone() // 我可以打电话!
SmartPhone.aboutPhone() // 关于本机
super
指向父类的原型对象时,无法通过super
调用父类实例上的方法或者属性
class Phone {
constructor (brand) {
this.brand = brand
}
}
class SmartPhone extends Phone {
constructor (brand) {
super(brand)
}
printBrand() {
console.log(`手机品牌:${super.brand}`)
}
}
var sp = new SmartPhone('华为') // 华为
sp.printBrand() // 手机品牌:undefined
- 在子类普通方法中通过
super
调用父类的方法时,方法内部的this
指向当前的子类实例,静态方法内部的this
指向当前的子类,而不是子类的实例
class Phone {
static color = '黑色'
constructor (brand) {
this.brand = brand
}
printBrand() {
console.log(`手机品牌:${this.brand}`)
}
}
class SmartPhone extends Phone {
constructor (brand) {
super(brand)
super.printBrand()
super.brand = '苹果'
super.printBrand()
}
printColor () {
console.log(super.color)
}
}
var p = new Phone('小米')
var sp = new SmartPhone('华为') // 华为; 苹果
sp.printColor() // 黑色
类的prototype属性和__proto__属性
Class
作为构造函数的语法糖,同时有prototype
属性和__proto__
属性,因此同时存在两条继承链
(函数也同时拥有prototype
和__proto__
两个属性,因为函数也是对象的一种,被称为函数对象)
子类的
__proto__
属性,表示构造函数的继承,总是指向父类
子类prototype
属性的__proto__
属性,表示方法的继承,总是指向父类的prototype属性
class phone {}
class SmartPhone extends Phone {}
console.log(SmartPhone.__proto__ === Phone) // true
console.log(SmartPhone.prototype.__proto__ === Phone.prototype) // true
// 实现模式
// B 的实例继承 A 的实例
// Object.setPrototypeOf(B.prototype, A.prototype);
// B 继承 A 的静态属性
// Object.setPrototypeOf(B, A);
// Object.setPrototypeOf方法的实现
// Object.setPrototypeOf = function(obj, proto) {
// obj.__proto__ = proto
// return obj;
// }