ES6 时代的面向对象编程

ES6 带给 JS 世界的是非常多的新便利。其中一个就是新的经典面向对象系统。

JS 传统上是不一样的

如果你熟悉 JS ,就会知道它本来是采用了一种基于原型( prototype )的面向对象机制。尽管理论上 JS 的原型机制要比传统的经典方式更灵活,因为原型是可以模拟后者。但是在实际使用中原型方式还有有些让人迷惑,容易出 bug 。

新的 ES6 OOP 系统就跟经典的 Ruby Python 或者 Scala 的方式一样了。下面我们来演示一下具体的操作。

构建类

新的 OOP 系统启用了原来很少用到的 class 保留字,用它来定义新类。

class Dog {
  constructor(name) {
    this.name = name
  }

  bark() {
    console.log('wuf! wuf!')
  }
}

var leo = new Dog('Leo')
leo.bark()

上面定义 Dog 类和它的 constructor ,还有一个实例方法可以让狗叫。

一个有意思的点是这里没有使用 function 关键字,也没有看到 prototype 。传统上,这个程序用 ES5 会写成这样。

function Dog(name) {
  this.name = name
}

Dog.prototype.bark = function() {
  console.log('wuf! wuf!')
}

var leo = new Dog('Leo')
leo.bark()

显然老方式有点不好理解。

继承

传统上用 prototype 的方式实现继承是比较让人迷惑的

  • 我该怎么去定义父类?
  • 子类中如何重新定义方法?
  • 如何向父类中传递参数?

这些东西都不太容易操作。但是新系统中使用 extends 和 super ,一切都简单了。

比如我们要定义一个 Dog 的子类 Labrador (拉普拉多)

class Labrador extends Dog {
  constructor(name) {
    super(name)
  }

  playWithKids() {
    console.log('playing with kids! wuf! wuf!')
  }
}

var leo = new Labrador('Leo')
leo.playWithKids()

但是如何来重新定义一个方法呢?比如拉普拉多要有自己的 bark 方法。

class Labrador extends Dog {
  constructor(name) {
    super(name)
  }

  bark() {
    console.log('wuf! wuf!')
    console.log('woof!')
  }

  playWithKids() {
    console.log('playing with kids! wuf! wuf!')
  }
}

var leo = new Labrador('Leo')
leo.bark()

非常之简单。我们甚至可以直接复用父类中的方法。

class Labrador extends Dog {
  constructor(name) {
    super(name)
  }

  bark() {
    super.bark()
    console.log('woof!')
  }

  playWithKids() {
    console.log('playing with kids! wuf! wuf!')
  }
}

var leo = new Labrador('Leo')
leo.bark()

同样非常简单明了。

Getters

新的 Getter 语法也很实用。比如有下面一个类:

class User {
  constructor(firstName, lastName, age) {
    this.firstName = firstName
    this.lastName = lastName
    this.age = age
  }
}

var user = new User('Igor', 'Sarcevic', 24)

如果我们要得到这个用户的全名,可以这样来做。

class User {
  constructor(firstName, lastName, age) {
    this.firstName = firstName
    this.lastName = lastName
    this.age = age
  }

  fullName() {
    return `${this.firstName} ${this.lastName}`
  }
}

var user = new User('Igor', 'Sarcevic', 24)
console.log(user.fullName())

fullName 这个方法挺简单的,但是每次执行的时候都要带括号。如果是用 Getter ,就是下面这样了。

class User {
  constructor(firstName, lastName, age) {
    this.firstName = firstName
    this.lastName = lastName
    this.age = age
  }

  get fullName() {
    return `${this.firstName} ${this.lastName}`
  }
}

var user = new User('Igor', 'Sarcevic', 24)

console.log(user.fullName)

实用 get 关键字,然后就可以直接 user.fullName 不要括号了。

参考