ʕ•ᴥ•ʔ RUNNINGJ

javascript代码整洁之道

来自Robert C. Martin的书,《clean code》. 目标是编写出可读的, 可重用, 可重构的代码。

变量

使用有意义、可读(出声)的变量名

坏: {% codeblock lang:javascript %} const yyyymmdstr = moment().format(‘YYYY/MM/DD’) {% endcodeblock %}

好: {% codeblock lang:javascript %} const now = moment().format(‘YYYY/MM/DD’) {% endcodeblock %}

对同类型的变量使用同样的词

坏: {% codeblock lang:javascript %} getUserInfo() getClientData() getCustomerRecord() {% endcodeblock %}

好: {% codeblock lang:javascript %} getUser() {% endcodeblock %}

使用可以搜索的名字

代码能够被读、被理解、被回忆、被搜索。

坏: {% codeblock lang:javascript %} // whate the heck is 864000000? setTimeout(blastOff, 864000000) {% endcodeblock %}

好: {% codeblock lang:javascript %} const MILLISECONDS_IN_A_DAY = 864000000 setTimeout(blastOff, MILLISECONDS_IN_A_DAY) {% endcodeblock %}

用可解释的变量民

坏: {% codeblock lang:javascript %} const address = “One Infinite Loop, Cupertino 95014”; const cityZipCodeRegex = /^[^,\]+[,\\s]+(.+?)\s*(\d{5})?$/; saveCityZipCode( address.match(cityZipCodeRegex)[1], // 第一个字段是什么 address.match(cityZipCodeRegex)[2] // 第二个字段是什么 ); {% endcodeblock %}

好: {% codeblock lang:javascript %} const address = “One Infinite Loop, Cupertino 95014”; const cityZipCodeRegex = /^[^,\]+[,\\s]+(.+?)\s*(\d{5})?$/; const [, city, zipCode] = address.match(cityZipCodeRegex) || []; saveCityZipCode(city, zipCode); {% endcodeblock %}

避免认知映射

坏: {% codeblock lang:javascript %} const locations = [“Austin”, “New York”, “San Francisco”]; locations.forEach(l => { doStuff(); doSomeOtherStuff(); // … // … // … // Wait, what is l for again? dispatch(l); }); {% endcodeblock %}

好: {% codeblock lang:javascript %} const locations = [“Austin”, “New York”, “San Francisco”]; locations.forEach(location => { doStuff(); doSomeOtherStuff(); // … // … // … dispatch(location); }); {% endcodeblock %}

不要增加额外的上下文前后缀

坏: {% codeblock lang:javascript %} const Car = { carMake: “Honda”, carModel: “Accord”, carColor: “Blue” };

function paintCar(car) {
  car.carColor = "Red";
}

{% endcodeblock %}

好: {% codeblock lang:javascript %} const Car = { make: “Honda”, model: “Accord”, color: “Blue” };

function paintCar(car) {
  car.color = "Red";
}

{% endcodeblock %}

使用参数默认值而不是短路条件

坏: {% codeblock lang:javascript %} function createMicrobrewery(name) { const breweryName = name || “Hipster Brew Co.”; // … } {% endcodeblock %}

好: {% codeblock lang:javascript %} function createMicrobrewery(name = “Hipster Brew Co.”) { // … } {% endcodeblock %}

函数

函数参数最好是2个以下

坏: {% codeblock lang:javascript %} function createMenu(title, body, buttonText, cancellable) { // … } {% endcodeblock %}

好: {% codeblock lang:javascript %} function createMenu({ title, body, buttonText, cancellable }) { // … }

createMenu({
  title: "Foo",
  body: "Bar",
  buttonText: "Baz",
  cancellable: true
});

{% endcodeblock %}

函数应该做一件

函数名应该指明函数做了什么

函数内容应该在同一个抽象层级

清理重复代码

用Object.assign设置默认对象

坏: {% codeblock lang:javascript %} const menuConfig = { title: null, body: “Bar”, buttonText: null, cancellable: true };

function createMenu(config) {
  config.title = config.title || "Foo";
  config.body = config.body || "Bar";
  config.buttonText = config.buttonText || "Baz";
  config.cancellable =
    config.cancellable !== undefined ? config.cancellable : true;
}

createMenu(menuConfig);

{% endcodeblock %}

好: {% codeblock lang:javascript %} const menuConfig = { title: “Order”, // User did not include ‘body’ key buttonText: “Send”, cancellable: true };

function createMenu(config) {
  config = Object.assign(
    {
      title: "Foo",
      body: "Bar",
      buttonText: "Baz",
      cancellable: true
    },
    config
  );

  // config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true}
  // ...
}

{% endcodeblock %}

不要有flag作为函数参数*

坏: {% codeblock lang:javascript %} function createFile(name, temp) { if (temp) { fs.create(./temp/${name}); } else { fs.create(name); } } {% endcodeblock %}

好: {% codeblock lang:javascript %} function createFile(name) { fs.create(name); }

function createTempFile(name) {
  createFile(`./temp/${name}`);
}

{% endcodeblock %}

避免副作用

参考函数式编程

不要写全局函数*

偏好函数式编程*

包装条件*

坏: {% codeblock lang:javascript %} if (fsm.state === “fetching” && isEmpty(listNode)) { // … } {% endcodeblock %}

好: {% codeblock lang:javascript %} function shouldShowSpinner(fsm, listNode) { return fsm.state === “fetching” && isEmpty(listNode); }

if (shouldShowSpinner(fsmInstance, listNodeInstance)) {
  // ...
}

{% endcodeblock %}

避免条件非*

坏: {% codeblock lang:javascript %} function isDOMNodeNotPresent(node) { // … }

if (!isDOMNodeNotPresent(node)) {
  // ...
}

{% endcodeblock %}

好: {% codeblock lang:javascript %} function isDOMNodePresent(node) { // … }

if (isDOMNodePresent(node)) {
  // ...
}

{% endcodeblock %}

避免条件

减少if/switch等的使用,通过别的地方的设计。

避免检查类型

删除dead code

其他

https://github.com/ryanmcdermott/clean-code-javascript