ES6 —(Class 的继承)
2017-08-24 21:23
846 查看
1、简介
Class 可以通过extends关键字实现继承。
class Point{ constructor(x, y){ this.x = x; this.y = y; } toString(){ return `( ${this.x}, ${this.y})` } } class ColorPoint extends Point{ constructor(x, y, color){ super(x, y); this.color = color; } toString(){ return `${this.color} ${super.toString()}`; } } var cp = new ColorPoint(1, 2, 'red'); console.log(cp.toString()); => red ( 1, 2)
注意
(1)如果在子类中显式定义了
constructor则子类必须在
constructor方法中调用
super方法,否则会报错。因为子类没有自己的
this对象,而是继承父类的
this对象,然后进行加工,如果不调用
super方法,子类就得不到
this对象。并且
this的使用是在
super方法调用之后。否则会报错。
class Poi{} class CPoi{ constructor(){} } var cpo = new CPoi(); => 报错 this is not defined -------------------------------------------------- class Poi{} class CPoi{ constructor(c){ this.c = c; super(); } } var cpo = new CPoi(); => 报错 this is not defined
(2)子类的实例对象既是 子类的实例 又是 父类的实例。
cp instanceof ColorPoint => true; cp instanceof Point => true;
(3)
Object.getPrototypeOf()方法可以用来从子类上获取父类。可以使用该方法判断,一个类是否继承了另一个类。
Object.getPrototypeOf(ColorPoint) === Point => true
2、super 关键字
super 这个关键字,既可以当作函数使用,又可以当作对象使用。在这两种情况下,它的用法完全不同。(1)super 作为函数调用
super 作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次
super()。
注意:
1)super 虽然代表了父类的构造函数,但是返回的是子类的实例,即 super 内的 this 指的是子类,因此
super()相当于
Point.prototype.constructor.call(this)。参见new.target属性。
2)作为函数时,
super()只能用在子类的构造函数中,用在其他地方将报错。
(2)super 作为对象
super 作为对象时,在普通方法中,指向父类的原型对象,在静态方法中,指向父类。
例如
ColorPoint的
toString方法中的
super.toString()就是将 super 当作一个对象使用,这时,super 在普通方法中,指向
Point.prototype,所以
super.toString()相当于
Point.prototype.toString()。
注意:
1)由于 super 在普通方法中指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过 super 调用的。
class A { constructor(){this.x = 2;}} class B extends A { getX() { return super.x;} } let b = new B(); b.getX() => undefined
如果属性定义在父类的原型对象上,super 就可以取到。
class A{} A.prototypy.x = 2;// 此时 B 中可通过 super.x 的到 x
2)ES6 规定,通过 super 调用父类的方法时, super 会绑定子类的 this。
3)由于绑定子类的 this ,所以如果通过 super 对某个属性赋值,这时 super 就是 this,赋值的属性会变成子类实例的属性。
class A { constructor(){ this.x = 2; } } class B extends A { constructor() { super(); this.x = 2; super.x = 3; console.log(super.x); // undefined console.log(this.x); // 3 } } let b = new B();
上述代码中,赋值时 super 就是 this ,即
super.x = 3 => this.x = 3,而当读取时 super 代表父类的原型对象,即
super.x => A.prototype.x返回
undefined。
4)使用 super 时,必须显式指定是作为函数,还是作为对象,否则会报错。
如果 super 作为对象,用在静态方法中,这时 super 将指向父类,而不是父类的原型对象。
3、类的 prototype 属性和 __proto__ 属性
每个对象都有__proto__属性,指向对应的构造函数的 prototype 属性。Class 作为构造函数的语法糖,同时有 prototype 属性和
__proto__属性,因此同时存在两条继承链。
(i)子类的
__proto__属性,表示构造函数的继承,总是指向父类。
(ii)子类 prototype 属性的
__proto__属性,表示方法的继承,总是指向父类的 prototype 属性。
B.__proto__ === A // true B.prototype.__proto__ = A.prototype // true
子类实例的
__proto__属性的
__proto__属性,指向父类实例的
__proto__属性。也就是说,子类的原型的原型,是父类的原型。
b.__proto__.__proto__ === a.__proto__ // true
(1)extends 的继承目标
extends关键字后面可以跟多种类型的值。
(1)函数
由于函数都有 prototype 属性(除了 Function.prototype 函数),因此,可以跟任何函数。
(2)Object
子类继承 Object 类,这种情况下,子类其实就是构造函数 Object 的复制, 子类的实例就是 Object 的实例。
class A extends Object {} A.__proto__ === Object // true A.prototype.__proto__ === Object.prototype // true
(3)不存在任何继承
这种情况下,A 作为一个基类(即不存在任何继承),就是一个普通函数,所以直接继承
Function.prototype。但是, A 调用后返回一个空对象(即 Object 实例),所以
A.prototype.__proto__指向构造函数(Object)的 prototype 属性。
class A {} A.__proto__ === Function.prototype // true A.prototype.__proto__ === Object.prototype // true
(4)子类继承 null
这种情况与第三种情况非常像。 A 也是一个普通函数,所以直接继承
Function.prototype。但是,A 调用后返回的一个 undefined 。
class A extends null {} A.__proto__ === Function.prototype // true A.prototype.__proto__ === undefined // true => 相当于 class A extends null { constructor(){ return Object.create(null);} }
4、原生构造函数的继承
原生构造函数是指语言内置的构造函数,通常用来生成数据结构。 ECMAScript 的原生构造函数大致有以下:Boolean()
Number()
String()
Array()
Date()
Function()
RegExp()
Error()
Object()
以前,这些原生构造函数无法继承,参见 ES5 的继承。ES6 允许使用
extends继承原生构造函数定义子类,因为 ES6 是先新建父类的实例对象 this ,然后在用子类的构造函数修饰 this ,使得父类的所有行为都可以继承。
注意:继承 Object 的子类,有一个行为差异。
class NewObj extends Object{ constructor(){ super( ...arguments); } } var o = new NewObject({attr: true}); o.attr === true // false
上面代码中,
NewObj继承了
Object,但是无法通过
super方法向父类
Object传参。这是因为 ES6 改变了
Object构造函数的行为,一旦发生
Object方法不是通过
new Object()这种形式调用,ES6 规定
Object构造函数会忽略参数。
5、Mixin 模式
Mixin 模式是指,将多个类的接口 “混入”(mix in)另一个类。 它在 ES6 的实现如下:function mix(...mixins){ class Mix{} for(let mixin of mixins){ copyPrototypes(Mix, mixin); copyPrototypes(Mix.prototype, mixin.prototype); } return Mix; } function copyPrototypes(target, source){ for(let key of Reflect.ownKeys(source)){ if( key !== "constructor" && key !== "prototype" && key !== "name" ){ let desc = Object.getOwnPrototyDescriptor(source, key); Object.defineProperty(target, key, desc); } } }
上面的 mix 函数,可以将多个对象合成一个类,使用的时候只要继承这个类即可。
class DistributedEdit extends mix(Loggable, Serializable) {}
阮一峰:ECMAScript 6入门
相关文章推荐
- ES6 Class继承中super在不同场景中的用法
- ES6 类(Class)的继承(extends)和自定义存(setter)取值(getter)详解
- 关于ES5中的prototype与ES6中class继承的比较
- ES6 Class的继承
- 04面向对象编程-02-原型继承 和 ES6的class继承
- JavaScript使用ES6的Class面向对象继承时 this is not defined 解决方法
- es6 Class的继承extends & super
- es6 javascript的Class 类的继承
- JavaScript面向对象编程之class继承(ES6新特性)
- ES6基础之Class的继承
- ES6 class,与js prototype原型继承有何关系?
- ES6 之 class 继承
- ES6------Class继承
- ES6 class的继承使用细节
- ES6新特性之类(Class)和继承(Extends)相关概念与用法分析
- ES6 Class 与prototype 继承等
- JavaScript 使用对象及ES6中的class
- C++ 内存布局(二) 虚继承 ---Empty virtual base classs (空虚基类)
- Debug:SSH继承时 could not find a getter for ... in class ... 异常的解决
- 13-14-15-16-面向对象、继承、封装、struct和class