您的位置:首页 > 其它

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入门
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: