1 引言

js也是具有OOP(object oriented programming)特性的,OOP在构建一些大型应用程序还是有一套成熟理论的。作为C++的使用者在学习js中的OOP特性的过程中,能够较快地理解其中的各种术语和概念,也能比较两种语言的异同,深化知识理解。通过js的OOP特性的学习也让我开始从语言层面考虑程序设计问题。
本篇文章主要介绍了js中的一些OOP特性,并且比较了js与C++的语言特性。如果你能熟练掌握C++的OOP特性,本文能帮助你快速地对js中的OOP特性建立整体的认识。

2 写给C++使用者的js中的OOP特性介绍

2.1 创建对象(object)

js中创建object的代码,示例如下:

let duck = {
  name: "Aflac",
  numLegs: 2,
  sayName: function() {
    return "The name of this duck is " + this.name + ".";
  }
};

js直接通过 {} 就可以创建出对象示例来,不需要对该对象(object)的类(class)进行声明。这点和C++不是很相同,C++需要先声明一个class再创建object。
这个object有两个成员变量和一个成员函数,需要注意的是这两个成员变量都是公有(public)的,他们是可以直接用 . 符号访问的。
js中也有 this 关键字,与C++相同, this 关键字用于表示当前实例。

2.2 类(class)的声明

js中声明一个类的操作实际上就是声明一个构造函数。

let Bird = function(name, color) {
  this.name = name;
  this.color = color;
  this.numLegs = 2;
}

let crow = new Bird("Alexis", "black");

crow instanceof Bird; // => true

上面的代码声明了Bird类,在js中通常类的名字都是由首字母大写的单词表示的。类的构造函数也能接受参数用于对实例的初始化,这点与C++非常相似,使用 new 关键字就能够创建该类的实例。
使用 instanceof 关键字用于检查对象是否属于某个类,也可通过验证 constructor 属性来判断一个对象是否属于一个类 crow.constructor == Bird

2.3 类的共有成员

js中通过 prototype 这一属性(把它叫做关键字好像还不太合适)能够实现C++中静态成员变量和静态成员函数的特性。

Bird.prototype.numLegs = 2;

上面的代码就给Bird类增加了一个静态成员变量。这个 prototype 可以是一个对象,这样类的共有成员就能方便地承载更多的属性了,示例代码如下。

Bird.prototype = {
  constructor: Bird,
  numLegs: 2,
  eat: function() {
    console.log("nom nom nom");
  },
  describe: function() {
    console.log("My name is " + this.name);
  }
};

需要注意的是需要设置好 constructor 属性,这样是为了保证代码逻辑的一致性。
对象会获得类的 prototype 属性,可以通过 isPrototypeof 方法来验证。

Bird.prototype.isPrototypeOf(duck);

2.4 类的私有成员

js与C++一样,也可以有私有成员变量,代码如下所示。 hatchedEgg 就相当与是Bird的私有成员变量,并且提供了修改这个成员变量的方法 getHatchedEggCount

function Bird() {
  let hatchedEgg = 10; // private property

  this.getHatchedEggCount = function() {
    // publicly available method that a bird object can use
    return hatchedEgg;
  };
}
let ducky = new Bird();
ducky.getHatchedEggCount(); // returns 10

这种形式在js中被称作闭包(closure),函数能够访问到与他处在同一个作用域(context)中的变量。

2.5 类的继承和派生

js中的派生主要通过 prototype 体现,下面的代码表示Bird派生自Object。同样,需要注意将 constructor 属性设置好。

Bird.prototype = Object.create(Animal.prototype);
Bird.prototype.constructor = Bird;

2.6 类的覆盖

js中可以重写基类中的方法,代码如下所示,这点与C++中的 override 相同。

function Animal() { }
Animal.prototype.eat = function() {
  return "nom nom nom";
};
function Bird() { }

// Inherit all methods from Animal
Bird.prototype = Object.create(Animal.prototype);

// Bird.eat() overrides Animal.eat()
Bird.prototype.eat = function() {
  return "peck peck peck";
};

Bird.prototype.fly = function() {
  console.log("I'm flying!");
};

通样也在派生之后也可以通过修改派生类的 prototype 以达到特化派生类的作用,上面的 fly 方法就是在Bird完成派生之后新增的方法。现在Bird有两个方法,它们分别是 eatfly