JavaScript 代码整洁之道(下)

目录

  1. 概述

  2. 变量

  3. 函数

  4. 对象和数据结构

  5. 测试

  6. 并发

  7. 错误处理

  8. 格式

  9. 注释

1、概述

一张幽默的图片:软件质量通过你在阅读代码的时候有多少报怨来进行评估

接着上一篇文章: JavaScript 代码整洁之道(上)

6、测试

测试比生产更重要。如果你不进行测试,或者测试的量不够,那你就不能肯定你写的代码不会造成破坏。测试数量依靠你的开发团队来决定,但 100% 覆盖率(所有语句和分支)能让你拥有巨大的信心,也能使程序员们安心。也就是说,你需要一个不错的测试框架,还需要一个好的覆盖检查工具.

没有什么理由可以让你不写测试。这里有 大量不错的 JS 测试框架,可以去找个你们团队喜欢的来用。如果你找一个适合在你的团队中使用的工作,就把为每个新产生的特性/方法添加测试作为目标。如果你喜欢测试驱动开发(TDD)的方法,非常好,但要注意在让你的测试覆盖所有特性,或者重构过的代码。

每次测试一个概念

不好:

const assert = require('assert');
describe('MakeMomentJSGreatAgain', function() {
  it('handles date boundaries', function() {
    let date;
    date = new MakeMomentJSGreatAgain('1/1/2015');
    date.addDays(30);
    date.shouldEqual('1/31/2015');
    date = new MakeMomentJSGreatAgain('2/1/2016');
    date.addDays(28);
    assert.equal('02/29/2016', date);
    date = new MakeMomentJSGreatAgain('2/1/2015');
    date.addDays(28);
    assert.equal('03/01/2015', date);
  });
});

好:

const assert = require('assert');
describe('MakeMomentJSGreatAgain', function() {
  it('handles 30-day months', function() {
    let date = new MakeMomentJSGreatAgain('1/1/2015');
    date.addDays(30);
    date.shouldEqual('1/31/2015');
  });
  it('handles leap year', function() {
    let date = new MakeMomentJSGreatAgain('2/1/2016');
    date.addDays(28);
    assert.equal('02/29/2016', date);
  });
  it('handles non-leap year', function() {
    let date = new MakeMomentJSGreatAgain('2/1/2015');
    date.addDays(28);
    assert.equal('03/01/2015', date);
  });
});

7、并发

使用 Promise 而不是回调

回调并不整洁,它会导致过多的嵌套。ES6 的 Promise 是个内置的全局类型。使用它!

不好:

require('request').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin', function(err, response) {
  if (err) {
    console.error(err);
  }
  else {
    require('fs').writeFile('article.html', response.body, function(err) {
      if (err) {
        console.error(err);
      } else {
        console.log('File written');
      }
    })
  }
})

好:

require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin')
  .then(function(response) {
    return require('fs-promise').writeFile('article.html', response);
  })
  .then(function() {
    console.log('File written');
  })
  .catch(function(err) {
    console.error(err);
  })

async/await 比 Promise 还整洁

与回调相当,Promise 已经相当整洁了,但 ES7 带来了更整洁的解决方案 —— async 和 await。你要做的事情就是在一个函数前加上 async 关键字,然后写下命令形式的逻辑,而不再需要 then 链。现在可以使用这个 ES7 特性带来的便利!

不好:

require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin')
  .then(function(response) {
    return require('fs-promise').writeFile('article.html', response);
  })
  .then(function() {
    console.log('File written');
  })
  .catch(function(err) {
    console.error(err);
  })

好:

async function getCleanCodeArticle() {
  try {
    var request = await require('request-promise')
    var response = await request.get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin');
    var fileHandle = await require('fs-promise');
    await fileHandle.writeFile('article.html', response);
    console.log('File written');
  } catch(err) {
      console.log(err);
    }
  }

8、错误处理

抛出错误是件好事!这表示运行时已经成功检测到程序出错了,它停止当前调用框上的函数执行,并中止进程(在 Node 中),最后在控制台通知你,并输出栈跟踪信息。

不要忽略捕捉到的错误

捕捉到错误却什么也不错,你就失去了纠正错误的机会。多数情况下把错误记录到控制台(console.log)也不比忽略它好多少,因为在少量的控制台信息中很难发现这一条。如果尝试在 try/catch 中封装代码,就意味着你知道这里可能发生错,你应该在错误发生的时候有应对的计划、或者处理办法。

不好:

try {
  functionThatMightThrow();
} catch (error) {
  console.log(error);
}

好:

try {
  functionThatMightThrow();
} catch (error) {
  // 选择之一(比 console.log 更闹心):
  console.error(error);
  // 另一个选择:
  notifyUserOfError(error);
  // 另一个选择:
  reportErrorToService(error);
  // 或者所有上述三种选择!
}

不要忽视被拒绝的Promise

这一条与不要忽略从 try/catch 捕捉到的错误有相同的原因。

不好:

getdata()
.then(data => {
  functionThatMightThrow(data);
})
.catch(error => {
  console.log(error);
});

好:

getdata()
.then(data => {
  functionThatMightThrow(data);
})
.catch(error => {
  // 选择之一(比 console.log 更闹心):
  console.error(error);
  // 另一个选择:
  notifyUserOfError(error);
  // 另一个选择:
  reportErrorToService(error);
  // 或者所有上述三种选择!
});

9、格式

格式是个很主观的东西,像这里提到的许多规则一,你不必完全遵循。要点不在于争论格式。大量工具 可以自动处理优化格式。用一个!让工程师争论格式问题简直就是在浪费时间和金钱。

对于那些不能自动处理的格式(可以自动处理的包括缩进、Tab或空格、双引号或单引用等),就看看这里的指导。

使用一致的大小写

JavaScript 是无类型的,所以大小写可以帮助你了解变量、函数等。这些规则具有较强的主观性,所以你的团队应该选择需要的。重点不在于你选择了什么,而在于要始终保持一致。

不好:

var DAYS_IN_WEEK = 7;
var daysInMonth = 30;
var songs = ['Back In Black', 'Stairway to Heaven', 'Hey Jude'];
var Artists = ['ACDC', 'Led Zeppelin', 'The Beatles'];
function eraseDatabase() {}
function restore_database() {}
class animal {}
class Alpaca {}

好:

var DAYS_IN_WEEK = 7;
var DAYS_IN_MONTH = 30;
var songs = ['Back In Black', 'Stairway to Heaven', 'Hey Jude'];
var artists = ['ACDC', 'Led Zeppelin', 'The Beatles'];
function eraseDatabase() {}
function restoreDatabase() {}
class Animal {}
class Alpaca {}

函数调用者和被调用者应该尽可能放在一起

如果一个函数调用另一个函数,那应该让他们在源文件中的位置非常接近。理想情况下应该把调用者放在被调用者的正上方,这会让你的代码更易读,因为我们都习惯从上往下读代码,就像读报纸那样。

不好:

class PerformanceReview {
  constructor(employee) {
    this.employee = employee;
  }
  lookupPeers() {
    return db.lookup(this.employee, 'peers');
  }
  lookupMananger() {
    return db.lookup(this.employee, 'manager');
  }
  getPeerReviews() {
    let peers = this.lookupPeers();
    // ...
  }
  perfReview() {
      getPeerReviews();
      getManagerReview();
      getSelfReview();
  }
  getManagerReview() {
    let manager = this.lookupManager();
  }
  getSelfReview() {
    // ...
  }
}
let review = new PerformanceReview(user);
review.perfReview();

好:

class PerformanceReview {
  constructor(employee) {
    this.employee = employee;
  }
  perfReview() {
      getPeerReviews();
      getManagerReview();
      getSelfReview();
  }
  getPeerReviews() {
    let peers = this.lookupPeers();
    // ...
  }
  lookupPeers() {
    return db.lookup(this.employee, 'peers');
  }
  getManagerReview() {
    let manager = this.lookupManager();
  }
  lookupMananger() {
    return db.lookup(this.employee, 'manager');
  }
  getSelfReview() {
    // ...
  }
}
let review = new PerformanceReview(employee);
review.perfReview();

10、注释

只注释业务逻辑复杂的内容

注释是用来解释代码的,而不是必须的。好的代码应该 自注释。

不好:

function hashIt(data) {
  // Hash 码
  var hash = 0;
  // 字符串长度
  var length = data.length;
  // 遍历数据中所有字符
  for (var i = 0; i < length; i++) {
    // 获取字符编码
    var char = data.charCodeAt(i);
    // 生成 Hash
    hash = ((hash << 5) - hash) + char;
    // 转换为32位整数
    hash = hash & hash;
  }
}

好:

function hashIt(data) {
  var hash = 0;
  var length = data.length;
  for (var i = 0; i < length; i++) {
    var char = data.charCodeAt(i);
    hash = ((hash << 5) - hash) + char;
    // 转换为32位整数
    hash = hash & hash;
  }
}

不要把注释掉的代码留在代码库中

版本控制存在的原因就是保存你的历史代码。

不好:

doStuff();
// doOtherStuff();
// doSomeMoreStuff();
// doSoMuchStuff();

好:

doStuff();

不需要日志式的注释

记住,使用版本控制!没用的代码、注释掉的代码,尤其是日志式的注释。用 git log 来获取历史信息!

不好:

/**
 * 2016-12-20: Removed monads, didn't understand them (RM)
 * 2016-10-01: Improved using special monads (JP)
 * 2016-02-03: Removed type-checking (LI)
 * 2015-03-14: Added combine with type-checking (JR)
 */
function combine(a, b) {
  return a + b;
}

好:

function combine(a, b) {
  return a + b;
}

避免位置标记

位置标记通常只会添加垃圾信息。通过对函数或变量名以及适当的缩进就能为代码带来良好的可视化结构。

不好:

////////////////////////////////////////////////////////////////////////////////
// Scope Model Instantiation
////////////////////////////////////////////////////////////////////////////////
let $scope.model = {
  menu: 'foo',
  nav: 'bar'
};
////////////////////////////////////////////////////////////////////////////////
// Action setup
////////////////////////////////////////////////////////////////////////////////
let actions = function() {
  // ...
}

好:

let $scope.model = {
  menu: 'foo',
  nav: 'bar'
};
let actions = function() {
  // ...
}

避免在源文件中添加版权注释

这是代码文件树顶层的 LICENSE 文件应该干的事情。

不好:

/*
The MIT License (MIT)
Copyright (c) 2016 Ryan McDermott
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
*/
function calculateBill() {
  // ...
}

好:

function calculateBill() {
  // ...
}




未经允许请勿转载:程序喵 » JavaScript 代码整洁之道(下)

点  赞 (0) 打  赏
分享到: