第六章: 行为委托 - 更好的语法

优质
小牛编辑
128浏览
2023-12-01

更好的语法

一个使 ES6 class 看似如此诱人的更好的东西是(见附录A来了解为什么要避免它!),声明类方法的速记语法:

  1. class Foo {
  2. methodName() { /* .. */ }
  3. }

我们从声明中扔掉了单词 function,这使所有的 JS 开发者欢呼!

你可能已经注意到,而且为此感到沮丧:上面推荐的 OLOO 语法出现了许多 function,这看起来像是对 OLOO 简化目标的诋毁。但它不必是!

在 ES6 中,我们可以在任何字面对象中使用 简约方法声明,所以一个 OLOO 风格的对象可以用这种方式声明(与 class 语法中相同的语法糖):

  1. var LoginController = {
  2. errors: [],
  3. getUser() { // 看,没有 `function`!
  4. // ...
  5. },
  6. getPassword() {
  7. // ...
  8. }
  9. // ...
  10. };

唯一的区别是字面对象的元素间依然需要 , 逗号分隔符,而 class 语法不必如此。这是在整件事情上很小的让步。

还有,在 ES6 中,一个你使用的更笨重的语法(比如 AuthController 的定义中):你一个一个地给属性赋值而不使用字面对象,可以改写为使用字面对象(于是你可以使用简约方法),而且你可以使用 Object.setPrototypeOf(..) 来修改对象的 [[Prototype]],像这样:

  1. // 使用更好的字面对象语法 w/ 简约方法!
  2. var AuthController = {
  3. errors: [],
  4. checkAuth() {
  5. // ...
  6. },
  7. server(url,data) {
  8. // ...
  9. }
  10. // ...
  11. };
  12. // 现在, 链接 `AuthController` 委托至 `LoginController`
  13. Object.setPrototypeOf( AuthController, LoginController );

ES6 中的 OLOO 风格,与简明方法一起,变得比它以前 友好得多(即使在以前,它也比经典的原型风格代码简单好看的多)。 你不必非得选用类(复杂性)来得到干净漂亮的对象语法!

没有词法

简约方法确实有一个缺点,一个重要的细节。考虑这段代码:

  1. var Foo = {
  2. bar() { /*..*/ },
  3. baz: function baz() { /*..*/ }
  4. };

这是去掉语法糖后,这段代码将如何工作:

  1. var Foo = {
  2. bar: function() { /*..*/ },
  3. baz: function baz() { /*..*/ }
  4. };

看到区别了?bar() 的速记法变成了一个附着在 bar 属性上的 匿名函数表达式function()..),因为函数对象本身没有名称标识符。和拥有词法名称标识符 baz,附着在 .baz 属性上的手动指定的 命名函数表达式function baz()..)做个比较。

那又怎么样?在 “你不懂 JS” 系列的 “作用域与闭包” 这本书中,我们详细讲解了 匿名函数表达式 的三个主要缺点。我们简单地重复一下它们,以便于我们和简明方法相比较。

一个匿名函数缺少 name 标识符:

  1. 使调试时的栈追踪变得困难
  2. 使自引用(递归,事件绑定等)变得困难
  3. 使代码(稍稍)变得难于理解

第一和第三条不适用于简明方法。

虽然去掉语法糖使用 匿名函数表达式 一般会使栈追踪中没有 name。简明方法在语言规范中被要求去设置相应的函数对象内部的 name 属性,所以栈追踪应当可以使用它(这是依赖于具体实现的,所以不能保证)。

不幸的是,第二条 仍然是简明方法的一个缺陷。 它们不会有词法标识符用来自引用。考虑:

  1. var Foo = {
  2. bar: function(x) {
  3. if (x < 10) {
  4. return Foo.bar( x * 2 );
  5. }
  6. return x;
  7. },
  8. baz: function baz(x) {
  9. if (x < 10) {
  10. return baz( x * 2 );
  11. }
  12. return x;
  13. }
  14. };

在这个例子中上面的手动 Foo.bar(x*2) 引用就足够了,但是在许多情况下,一个函数不一定能够这样做,比如使用 this 绑定,函数在委托中被分享到不同的对象,等等。你将会想要使用一个真正的自引用,而函数对象的 name 标识符是实现的最佳方式。

只要小心简明方法的这个注意点,而且如果当你陷入缺少自引用的问题时,仅仅为这个声明 放弃简明方法语法,取代以手动的 命名函数表达式 声明形式:baz: function baz(){..}