从零开始学_JavaScript_系列(65)——class的继承(2)super、extends与多重继承
2017-09-08 14:12
441 查看
4、super关键字
4.1、作为函数时使用
当super作为函数时使用,非常简单。只允许在子类的构造函数中调用;
并且需要在调用this之前调用;
而且必须调用一次(除非你省略掉子类的构造函数);
如果不符合以上要求,那么就会报错,以下是标准写法
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 模式的实现
相关文章推荐
- 从零开始学_JavaScript_系列(64)——class的继承(1)基本概念、继承构造函数和class
- es6 Class的继承extends & super
- 从零开始学_JavaScript_系列(60)——class(1)基本概念
- 从零开始学_JavaScript_系列(35)——继承与遍历的对照表
- 从零开始学_JavaScript_系列(61)——class(2)私有方法、this
- 从零开始学_JavaScript_系列(62)——class(3)setter和getter、Generator、async函数
- 从零开始学_JavaScript_系列(18)——dojo(7)(dojo中类的继承)
- 从零开始学_JavaScript_系列(56)——Generator函数(4)简写,this与继承
- 从零开始学_JavaScript_系列(63)——class(4)静态方法和new.target
- 从零开始学_JavaScript_系列(42)——简述js的八种继承方式
- [jvm解析系列][六]class里的常量池,访问标志,类的继承关系,如何把一个类在字节码中描述清楚?
- javascript class 继承
- java继承extends与super关键字理解
- 多重继承头文件包含时导致class类型重定义的解决办法
- javascript 面向对象,实现namespace,class,继承,重载
- 深入理解JavaScript系列(二): 原型、原型链与继承
- 从零开始学_JavaScript_系列(13)——CSS<2>(新窗口打开,背景图片填充,底文字,小提示,CSS选择器整理)
- 从零开始学_JavaScript_系列(四)——jquery(基础,选择器,触发条件,动画,回调函数)
- ES6 类(Class)的继承(extends)和自定义存(setter)取值(getter)详解
- 继承 关键字extends 实例package com.cjg.test; public class Animal { private String name; private int age;