文档结构  
翻译进度:46%     翻译赏金:0 元 (?)    ¥ 我要打赏
参与翻译: Kun (5), Render (1)

“class”构造允许用干净,漂亮的语法来定义基于原型的类。

“class” 语法

class语法是多功能的,我们将首先从一个简单的例子开始.

这是一个基于原型的User类:

function User(name) {
  this.name = name;
}

User.prototype.sayHi = function() {
  alert(this.name);
}

let user = new User("John");
user.sayHi();

...这和使用类语法是一样的:

class User {

  constructor(name) {
    this.name = name;
  }

  sayHi() {
    alert(this.name);
  }

}

let user = new User("John");
user.sayHi();
第 1 段(可获 0.5 积分)

很明显,两个种写法非常像。不过请注意,类里的方法之间是没有逗号的。新手经常会忘了这一点并类的方法后面加上逗号,这样是错误的。这是两种写法的一个区别。

那么,类到底是做什么的?如果你会认为它定义了一个新实例,那么你错了。

这里的类User做了两件事:

  1. 声明一个变量User,引用函数"constructor"。
  2. 把所有的方法都加入到User.prototype. 也就是 sayHiconstructor。
第 2 段(可获 1.24 积分)

下面一段代码挖掘class背后的机制:

class User {
  constructor(name) { this.name = name; }
  sayHi() { alert(this.name);  }
}

// 证据: User 就是"constructor"函数
alert(User == User.prototype.constructor); // true

// 证据: "prototype"里有两个方法
alert(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi

下图展示了User类是如何创建的:

所以class只是一个把构造函数和原型方法一起声明的一个特殊语法。

当然不仅仅如此,还有一些如下一些小变化:

第 3 段(可获 0.55 积分)

构造函数要搭配new使用

与常规函数不同,class的构造函数只能和new搭配使用:

class User {
  constructor() {}
}

alert(typeof User); // function
User(); // Error: Class constructor User cannot be invoked without 'new'

不同的字符串表示

如果我们使用类似alert(User)来输出函数User,一般会看到"class User...",而函数则会是"function User...".

不过不要困惑:虽然字符串表示会有区别,但是说到底它还是个函数,JavaScript里并没有类这么个“实体”。

类方法不可枚举

类定义会把所有的类方法的enumerable置为false。这样有一个好处:我们用for...in去遍历对象时,就不会遍历到类方法了。

第 4 段(可获 1.05 积分)

类会生成默认的constructor

如果构造类时,没有实现contructor,系统会生成一个空函数作为constructor,跟我们写constructor() {}一样的效果

类自动启用strict模式

所有class下的代码都要符合strict模式的规范

Getters/setters

类还可以实现getters/setters. 下面是一个例子:

class User {

  constructor(name) {
    // invokes the setter
    this.name = name;
  }

  get name() {
    return this._name;
  }

  set name(value) {
    if (value.length < 4) {
      alert("Name too short.");
      return;
    }
    this._name = value;
  }

}

let user = new User("John");
alert(user.name); // John

user = new User(""); // Name too short.
第 5 段(可获 0.65 积分)

在内部,getters和setters是用如下方式实现的:

Object.defineProperty(User.prototype, {
  name: {
    get() {
      return this._name
    },
    set(name) {
      // ...
    }
  }
});

只有方法

跟对象不一样,类声明里是不允许声明属性的,只会有方法和getters/setters。当然我们还是有办法突破这个限制的,只是需要一些额外的步骤。

如果确实需要非函数的值,我们可以手动调整原型prototype,如下:

第 6 段(可获 0.85 积分)
class User { }

User.prototype.test = 5;

alert( new User().test ); // 5

So, technically that’s possible, but we should know why we’re doing it. Such properties will be shared among all objects of the class.

An “in-class” alternative is to use a getter:

class User {
  get test() {
    return 5;
  }
}

alert( new User().test ); // 5

From the external code, the usage is the same. But the getter variant is a bit slower.

Class Expression

Just like functions, classes can be defined inside another expression, passed around, returned etc.

Here’s a class-returning function (“class factory”):

第 7 段(可获 0.91 积分)
function makeClass(phrase) {
  // declare a class and return it
  return class {
    sayHi() {
      alert(phrase);
    };
  };
}

let User = makeClass("Hello");

new User().sayHi(); // Hello

That’s quite normal if we recall that class is just a special form of a function-with-prototype definition.

And, like Named Function Expressions, such classes also may have a name, that is visible inside that class only:

// "Named Class Expression" (alas, no such term, but that's what's going on)
let User = class MyClass {
  sayHi() {
    alert(MyClass); // MyClass is visible only inside the class
  }
};

new User().sayHi(); // works, shows MyClass definition

alert(MyClass); // error, MyClass not visible outside of the class
第 8 段(可获 0.45 积分)

Static methods

We can also assign methods to the class function, not to its "prototype". Such methods are called static.

An example:

class User {
  static staticMethod() {
    alert(this == User);
  }
}

User.staticMethod(); // true

That actually does the same as assigning it as a function property:

function User() { }

User.staticMethod = function() {
  alert(this == User);
};

The value of this inside User.staticMethod() is the class constructor User itself (the “object before dot” rule).

Usually, static methods are used to implement functions that belong to the class, but not to any particular object of it.

第 9 段(可获 0.85 积分)

For instance, we have Article objects and need a function to compare them. The natural choice would be Article.compare, like this:

class Article {
  constructor(title, date) {
    this.title = title;
    this.date = date;
  }

  static compare(articleA, articleB) {
    return articleA.date - articleB.date;
  }
}

// usage
let articles = [
  new Article("Mind", new Date(2016, 1, 1)),
  new Article("Body", new Date(2016, 0, 1)),
  new Article("JavaScript", new Date(2016, 11, 1))
];

articles.sort(Article.compare);

alert( articles[0].title ); // Body
第 10 段(可获 0.24 积分)

Here Article.compare stands “over” the articles, as a means to compare them. It’s not a method of an article, but rather of the whole class.

Another example would be a so-called “factory” method. Imagine, we need few ways to create an article:

  1. Create by given parameters (title, date etc).
  2. Create an empty article with today’s date.

The first way can be implemented by the constructor. And for the second one we can make a static method of the class.

Like Article.createTodays() here:

class Article {
  constructor(title, date) {
    this.title = title;
    this.date = date;
  }

  static createTodays() {
    // remember, this = Article
    return new this("Todays digest", new Date());
  }
}

let article = Article.createTodays();

alert( article.title ); // Todays digest
第 11 段(可获 1 积分)

Now every time we need to create a todays digest, we can call Article.createTodays(). Once again, that’s not a method of an article, but a method of the whole class.

Static methods are also used in database-related classes to search/save/remove entries from the database, like this:

// assuming Article is a special class for managing articles
// static method to remove the article:
Article.remove({id: 12345});

Summary

The basic class syntax looks like this:

class MyClass {
  constructor(...) {
    // ...
  }
  method1(...) {}
  method2(...) {}
  get something(...) {}
  set something(...) {}
  static staticMethod(..) {}
  // ...
}
第 12 段(可获 0.7 积分)

The value of MyClass is a function provided as constructor. If there’s no constructor, then an empty function.

In any case, methods listed in the class declaration become members of its prototype, with the exception of static methods that are written into the function itself and callable as MyClass.staticMethod(). Static methods are used when we need a function bound to a class, but not to any object of that class.

In the next chapter we’ll learn more about classes, including inheritance.

第 13 段(可获 0.98 积分)

文章评论