ES6 class类简单用法纪录

116 阅读4分钟

一、什么是“类”?

所谓“类”就是对象的模板,对象就是“类”的实例。但是,JavaScript 语言的对象体系,不是基于“类”的,而是基于构造函数(constructor)和原型链(prototype)。所谓”构造函数”,就是专门用来生成实例对象的函数。

构造函数特点:

①、首字母大写

②、函数体内部使用了this关键字,代表了所要生成的对象实例

③、生成对象的时候,必须使用new命令

那什么是new命令呢?

new命令的作用,就是执行构造函数,返回一个实例对象。

var Test = function(){  //Test就是一个构造函数
    this.name = 'niubi'
}
var a = new Test() // a就是一个实例对象

new的原理是什么,创建一个新对象需要几步?

①、创建一个空对象,作为将要返回的对象实例

let newObject = {}

②、将这个空对象的原型,指向构造函数的prototype属性

newObject.__prototype__ = XX.prototype

③、将这个空对象赋值给函数内部的this关键字

XX.apply(obj)

④、返回新对象

构造函数作为模板,可以生成实例对象。但是,有时拿不到构造函数,只能拿到一个现有的对象。我们希望以这个现有的对象作为模板,生成新的实例对象,这时就可以使用Object.create()方法。

二、“类”的写法

引用阮神的例子:

function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function () {
  return '(' + this.x + ', ' + this.y + ')';
};

var p = new Point(1, 2);

使用ES6 class复写一遍:

class Point {
  constructor(x, y) {  //构造方法,返回实例对象即this
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}
typeof Point // "function"
Point === Point.prototype.constructor // true 证明类的数据类型就是函数,类本身就指向构造函数

三、“类”内部的this指向问题

默认指向类的实例。

如果在静态方法中,this指的是类,而不是实例。

四、class类的静态方法-static

static关键字一般作用于类的方法,用来定义一个工具函数。static方法不能被实例对象调用,只能通过类名来调用。同时static方法也可以被继承,而且也能在子类中用super对象来调用父类中的static方法。

class Person{      //没有constructor的类会默认生成一个constructor构造器
      static sayName(){
          console.log("我是static函数")
      }
}
class Student extends Person{}
const student = new Student()
Person.sayName() //我是static函数
Student.sayName() //我是static函数
student.sayName() //用实例化的对象来调用static方法时,代码会报错

static的this指向问题:

class Foo {
  static bar() {
    this.baz();
  }
  static baz() {
    console.log('hello');
  }
  baz() {
    console.log('world');
  }
}

const poo = new Foo()
Foo.bar() // hello
Foo.baz() // hello
poo.baz() // world
poo.bar() // 报错

参考阮神的例子,发现其中三点:

①、静态方法可以与非静态方法重名

②、无关顺序,会优先调用static baz方法

③、this的指向问题

static的继承问题:

static静态方法同样可以被继承,使用方法为利用super()直接调用

五、实例对象自身属性的新写法

旧写法:

class Test {
    constructor(){
        this.num = 00
    }
}

新写法:

class Test {
    num = 0
}

优点:更加清晰可见,代码整齐

六、class类的继承

依旧使用阮神的例子:

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

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

ES6规定,子类必须在constructor方法中调用super方法

class ColorPoint extends Point {
}

// 等同于
class ColorPoint extends Point {
  constructor(...args) {
    super(...args);
  }
}

七、super关键字的使用

super()只能用在子函数内部,用在其他地方会报错,有两种用法:

①、当作函数使用

class A {
  constructor() {
    console.log(new.target.name);
  }
}
class B extends A {
  constructor() {
    super();
  }
}
new A() // A
new B() // B

子类B的构造函数之中的super(),代表调用A构造函数。但是返回的是子类B的实例,即super内部的this指的是B的实例,因此super()在这里相当于A.prototype.constructor.call(this)

②、当作对象使用

class A {
  p() {
    return 2;
  }
}

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

上面代码中,子类B当中的super.p(),就是将super当作一个对象使用。这时,super在普通方法之中,指向A.prototype,所以super.p()就相当于A.prototype.p()

注意:由于**super**指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过**super**调用的

class A {
  constructor() {
    this.p = 2;
  }
}

class B extends A {
  get m() {
    return super.p;
  }
}

let b = new B();
b.m // undefined

那么问题来了,里面的get是什么作用呢?

取值函数(getter)和存值函数(setter) 

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

let inst = new MyClass();

inst.prop = 123;
// setter: 123

inst.prop
// 'getter'

当子类调用父类方法时,如果父类对象内存在static方法名称与非静态名称相同,实例对象调用非静态,子类调用static。