天天看点

ES6:Class与继承

class

在ES5中,

function Person(name,age){
 this.name=name;
 this.age=age;
}
Person.prototype.showName=function(){
 console.log(this.name+","+this.age) ;
}
var s=new Person("op",20);
s.showName();
function Student(cose){
 this.cose=cose;
 this.showCode=function(){
  console.log("hello"+cose);
 }
}
var s1=new Student('for');
s1.showCode();
Student.prototype=new Person('some',18);//继承
           

在ES6中,

let show='showAge';  
class Person{
 constructor(name,age){
  this.name=name;
  this.age=age;
 }
 showName(){
  console.log(this.name+this.age);
 }
 [show](){
  console.log("dddd");
 }
}
var p1=new Person("we",20);
p1.showName();
p1.showAge();//ddd
           

constructor 方法

class Point {
}

// 等同于
class Point {
  constructor() {}
}
           

类的所有实例共享一个原型对象

var p1 = new Point(2,3);
var p2 = new Point(3,2);

p1.__proto__ === p2.__proto__//true
           

上面代码中,p1和p2都是Point的实例,它们的原型都是

Point.prototype

,所以

__proto__

属性是相等的。

有get、set方法

class MyClass {
  constructor() {
    // ...
  }
  get prop() {
    return 'getter';
  }
  set prop(value) {
    console.log('setter: '+value);
  }
}
           

类的属性名,可以采用表达式。

let methodName = 'getArea';

class Square {
  constructor(length) {
    // ...
  }

  [methodName]() {
    // ...
  }
}
           

类也可以使用表达式的形式定义

const MyClass = class Me {
  getClassName() {
    return Me.name;
  }
};
           

注意:

  1. 类和模块的内部,默认就是严格模式,所以不需要使用

    use strict

    指定运行模式。
  2. 类不存在变量提升(hoist)
  3. 由于本质上,ES6 的类只是 ES5 的构造函数的一层包装,所以函数的许多特性都被Class继承,包括

    name

    属性。
  4. 如果某个方法之前加上星号(

    *

    ),就表示该方法是一个

    Generator

    函数。

this 的指向:

  • 类的方法内部如果含有

    this

    ,它默认指向类的实例。但是,必须非常小心,一旦单独使用该方法,很可能报错。

报错解决方法:

  1. 在构造方法中绑定

    this

    ,这样就不会找不到

    print

    方法了。
class Logger {
  constructor() {
    this.printName = this.printName.bind(this);
  }
  //。。。
}
           
  1. 使用箭头函数。
class Obj {
  constructor() {
    this.getThis = () => this;
  }
}
const myObj = new Obj();
myObj.getThis() === myObj // true
           
  1. 使用

    Proxy

    ,获取方法的时候,自动绑定

    this

静态方法

加上

static

关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

  1. 注意,如果静态方法包含

    this

    关键字,这个

    this

    指的是类,而不是实例。
class Foo {
  static bar() {
    this.baz();
  }
  static baz() {
    console.log('hello');
  }
  baz() {
    console.log('world');
  }
}

Foo.bar() // hello
           

上面代码中,静态方法

bar

调用了

this.baz

,这里的

this

指的是

Foo

类,而不是

Foo

的实例,等同于调用

Foo.baz

。另外,从这个例子还可以看出,静态方法可以与非静态方法重名。

  1. 父类的静态方法,可以被子类继承。
  2. 静态方法也是可以从

    super

    对象上调用的。
class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
  static classMethod() {
    return super.classMethod() + ', too';
  }
}

Bar.classMethod() // "hello, too"
           

实例属性除了定义在

constructor()

方法里面的this上面,也可以定义在类的最顶层。

静态属性指的是 Class 本身的属性,即

Class.propName

,而不是定义在实例对象(

this

)上的属性。

new.target

属性

new

是从构造函数生成实例对象的命令。ES6 为

new

命令引入了一个

new.target

属性,该属性一般用在构造函数之中,返回new命令作用于的那个构造函数。如果构造函数不是通过new命令或

Reflect.construct()

调用的,

new.target

会返回

undefined

,因此这个属性可以用来确定构造函数是怎么调用的。

function Person(name) {
  if (new.target !== undefined) {
    this.name = name;
  } else {
    throw new Error('必须使用 new 命令生成实例');
  }
}

// 另一种写法
function Person(name) {
  if (new.target === Person) {
    this.name = name;
  } else {
    throw new Error('必须使用 new 命令生成实例');
  }
}

var person = new Person('张三'); // 正确
var notAPerson = Person.call(person, '张三');  // 报错
           

Class 内部调用

new.target

,返回当前 Class。

class Rectangle {
  constructor(length, width) {
    console.log(new.target === Rectangle);
    this.length = length;
    this.width = width;
  }
}

var obj = new Rectangle(3, 4); // 输出 true
           

子类继承父类时,

new.target

会返回子类。

class Rectangle {
  constructor(length, width) {
    console.log(new.target === Rectangle);
    // ...
  }
}

class Square extends Rectangle {
  constructor(length, width) {
    super(length, width);
  }
}

var obj = new Square(3); // 输出 false
           

利用这个特点,可以写出不能独立使用、必须继承后才能使用的类。

class Shape {
  constructor() {
    if (new.target === Shape) {
      throw new Error('本类不能实例化');
    }
  }
}
class Rectangle extends Shape {
  constructor(length, width) {
    super();
    // ...
  }
}
var x = new Shape();  // 报错
var y = new Rectangle(3, 4);  // 正确
           

继承

Class 可以通过

extends

关键字实现继承

class Point {
}

class ColorPoint extends Point {
}
           
class ColorPoint extends Point {
  constructor(x, y, color) {
    super(x, y); // 调用父类的constructor(x, y)
    this.color = color;
  }

  toString() {
    return this.color + ' ' + super.toString(); // 调用父类的toString()
  }
}
           

Object.getPrototypeOf

方法可以用来从子类上获取父类。可以使用这个方法判断,一个类是否继承了另一个类。

super

这个关键字

super

这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同。

  1. 第一种情况,

    super

    作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数。
class A {
  constructor() {
    console.log(new.target.name);
  }
}
class B extends A {
  constructor() {
    super();
  }
}
new A() // A
new B() // B
           

作为函数时,

super()

只能用在子类的构造函数之中,用在其他地方就会报错。

  1. 第二种情况,

    super

    作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
class A {
  p() {
    return 2;
  }
}

class B extends A {
  constructor() {
    super();
    console.log(super.p()); // 2
  }
}

let b = new B();
           

类的

prototype

属性和

__proto__

属性

(1)子类的_

_proto__

属性,表示构造函数的继承,总是指向父类。

(2)子类prototype属性的

__proto__

属性,表示方法的继承,总是指向父类的

prototype

属性。

class A {
}

class B extends A {
}

B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true
           

第一种,子类继承Object类。

class A extends Object {
}

A.__proto__ === Object // true
A.prototype.__proto__ === Object.prototype // true
           

第二种情况,不存在任何继承。

class A {
}

A.__proto__ === Function.prototype // true
A.prototype.__proto__ === Object.prototype // true
           
es6