4、super关键字

4.1、作为函数时使用

当super作为函数时使用,非常简单。

  1. 只允许在子类的构造函数中调用;
  2. 并且需要在调用this之前调用;
  3. 而且必须调用一次(除非你省略掉子类的构造函数);

如果不符合以上要求,那么就会报错,以下是标准写法

class Foo {
}

class Bar extends Foo {
constructor() {
super()
}
}

4.2、作为对象使用

当super关键字作为对象使用时,有几点比较特殊:

1、首先,必须放在类的函数里使用;
2、并且不能只使用super,不然在声明的时候就会报错;

原因是不知道你是把super是当做函数使用,还是当做对象使用。

class Foo {
logFoo() {
super; // Uncaught SyntaxError: 'super' keyword unexpected here
}
}

3、因此必须通过super.xx这种方式来调用;

class Foo {
logFoo() {
return super.abc;
}
}
(new Foo()).logFoo() // undefined

4、super在作为对象使用时,又分为两种情况:

4.1、当位于类的普通方法时,指向父类的prototype属性(但由于不能单独调用super,因此这个无法直接通过等式来验证)。

变相验证方式如下:

class Foo {
logFoo() {
console.log("Foo.log")
}

static staticFoo() {
console.log("Foo.static")
}
}
class Bar extends Foo {
logBar() {
console.log('Bar.log')
}

static staticBar() {
console.log("Bar.static")
}

runSuper(key) {
try {
super[key]()
} catch (err) {
console.log("ERROR: " + err)
}
}
}
let bar = new Bar()
bar.runSuper('logFoo') // Foo.log
bar.runSuper('staticFoo') // ERROR: TypeError: (intermediate value)[key] is not a function
bar.runSuper('logBar') // ERROR: TypeError: (intermediate value)[key] is not a function
bar.runSuper('staticBar') // ERROR: TypeError: (intermediate value)[key] is not a function

以上代码表示,除了Foo本身上的方法之外,无论是Foo的静态方法,或者是Bar的普通/静态方法,都不存在。

因此可以变相说明super关键字在作为对象时,直接指向父类的prototype属性。

大约是(之所以说大约,是跟浏览器实现有关,我没具体看标准):

super === Foo.prototype

4.2、当位于类的静态方法中时,指向父类。

和上面类似,验证代码如下:

class Foo {
logFoo() {
console.log("Foo.log")
}

static staticFoo() {
console.log("Foo.static")
}
}
class Bar extends Foo {
logBar() {
console.log('Bar.log')
}

static staticBar() {
console.log("Bar.static")
}

static runSuper(key) {
try {
super[key]()
} catch (err) {
console.log("ERROR: " + err)
}
}
}

Bar.runSuper('logFoo') // ERROR: TypeError: (intermediate value)[key] is not a function
Bar.runSuper('staticFoo') // Foo.static
Bar.runSuper('logBar') // ERROR: TypeError: (intermediate value)[key] is not a function
Bar.runSuper('staticBar') // ERROR: TypeError: (intermediate value)[key] is not a function

会发现只能调用到父类的静态方法。而只能调用到父类静态方法的对象,只有父类本身。

因此大约是以下(之所以说大约,是跟浏览器实现有关,我没具体看标准):

super === Foo

5、那么剩下最后一个问题,当通过super调用父类的方法时,this指向谁?

5.1、作为类的普通方法时,this指向子类的实例

class Foo {
foo() {
return this
}
}
class Bar extends Foo {
runSuper(key) {
return super[key]()
}
}

let p = new Bar()
p.runSuper('foo') === p // true

如代码,this指向子类的实例。

不过想想也正常,假如不指向子类的实例的话,那么通过this调用的一些子类的方法、变量等,那么是会报错的。

这也是es6的规定。

5.2、作为静态方法时,this指向子类

class Foo {
static staticFoo() {
return this
}
}
class Bar extends Foo {
static runSuper(key) {
return super[key]()
}
}

Bar.runSuper('staticFoo') === Bar // true

5、extends关键字

我们在上面讨论子类继承不同父类的时候,提到extends关键字。

并且在继承一个普通的构造函数时,也可以通过extends关键字来继承。

那么,可以通过extends关键字继承其他类型么?

答案当然是可以的,但仅限于部分。

5.1、继承Object对象;

class Bar extends Object {
}

讲道理说,继承这个,效果就是让Bar.__proto__ === Object,因此可以让Bar调用Object的原生方法。

可以算是对Object对象的扩展吧,如果对哪些方法不满意,可以写在Bar上,不影响Object对象本身。

但由于Object.prototype属性上没有什么特殊的东西,因此这样继承,一般没必要使用子类的实例(因为子类实例本身无法调用Object的方法)

5.2、继承Funtion

在分析这个之前,先分析一波普通函数的继承。在分析普通函数的继承之前,分析一波Function这个内置对象的原型和原型链。

前注:prototype是原型,__proto__是原型链

如代码和注释:

// Function的原型链指向Function的原型
Function.__proto__ === Function.prototype // true
// Function原型的原型链,指向Object的原型
Function.prototype.__proto__ === Object.prototype // true

以上说明Function继承于Object的原型

然后分析class的继承,如代码和注释:

class Foo {
}
// 说明类的原型链指向Function的原型链,相当于类Foo的构造函数就是Function
Foo.__proto__ === Function.prototype // true
Foo.__proto__ === Function.__proto__ // true
Object.prototype.toString.call(Foo) // "[object Function]"
Foo.constructor === Function // true

// 但类Foo的原型显然指向他自己的原型链(是一个对象),所以以下是false
Foo.prototype === Function.prototype // false
// 因为Foo.prototype是对象,所以对象的原型链向对象的原型
Foo.prototype.__proto__ === Object.prototype

从以上结论可以得出,Foo是Function的实例(而非Function本身)

而假如类继承Function对象的话,是一个标准的继承,代码如下:

class Bar extends Function {
}
// 原型链指向对象本身
Bar.__proto__ === Function // true
// 原型的原型链指向函数的原型
Bar.prototype.__proto__ === Function.prototype //true

分析:

1、因为Bar的原型链指向Function,因此Bar可以调用Function上的方法,也可以调用Function原型链上的方法(不继承的时候也可以调用原型链上的方法);

2、因为Bar的原型的原型链指向Function的原型,因此Bar的实例的原型链的原型链指向Function的原型。

3、所以当类继承Function的时候,最大不同之处在于,类的实例可以调用被继承对象的原型链上的方法,放在这里就是可以使用Function原型链上的方法(比如apply或者call),而正常是不行的

let p = new Bar()
p.apply === Function.apply // true

6、多重继承

多重继承其实也不难,明白单一继承的方法,就好了。

多重继承有两种:

1、不算多重继承的多重继承

比如类D要继承类A、B、C,那么先让一个空类继承类A,再让返回的结果继承类B,再让返回的结果继承类C,然后再让类D继承这个返回的结果。

这个的优点是可以按继承顺序执行每个类的构造函数。

代码略

2、通过mixin方式的继承

还记不记得单继承是怎么做的?两条原型链,外加一个构造函数。

先创造一个新的空类,然后通过mixin的方式来将类A、B、C的prototype__proto__属性合并到一起(就是Object.getOwnPropertyDescriptor拿各个属性,然后再通过Object.defineProperty全部添加到空类里),生成一个新类。

然后让新类被类D继承即可。

不过这种缺点是类A、B、C的构造函数没法执行(类的构造函数只能通过new这种方式来执行)。

懒得自己写代码了,给一个阮一峰的博客里的代码吧。

Mixin 模式的实现

更多相关文章

  1. 如何知道Object是否为String类型对象?
  2. 如何将树路径转换为json对象
  3. OOP面向对象编程(一)-------方法的重载
  4. Java学习(三)面向对象之多态
  5. 黑马程序员——Java学习笔记 String类和基本数据类型对象包装类
  6. JAVA-初步认识-第十章-对象的初始化过程
  7. java类与对象,用程序解释
  8. 孔维滢201771010110《面向对象程序设计(java)》第一周学习总结
  9. Xstream把xml转换成java对象的异常:java.lang.NoClassDefFoundErr

随机推荐

  1. Laravel手把手系列教程之一环安装和环境
  2. php和django位于同一个lighttpd服务器上
  3. 如何加载json文件?
  4. 将node.js服务器更改为Apache服务器
  5. 纯真ip数据库查询的php实现(补充分组查询)
  6. thinkPHP5下扩展encryptedData解密算法文
  7. 用户GROUP BY ERROR之间的SQL查询private
  8. JSON解析错误:无法识别的标记'<'处于角度
  9. PHP的页面布局怎样设计
  10. xhgui不显示php本机功能