【译】JS代码整洁之道——快速最佳实践

1,622 阅读6分钟

代码整洁之道使你的代码更易被人理解

简介

如果你不仅在乎代码是否正常运行,还关心代码本身的编写质量,那么可以说你在追求代码的整洁之道。

_专业的开发人员将为未来的自我和“其他人”编写代码,而不仅仅是为机器编写代码_

基于上述讨论,代码的整洁之道被定义为以不言自明,通俗易懂,且易于修改和拓展的方式进行代码编写。

甚至,错误的代码都能运行,但代码不整洁真的会让开发团队为之头疼

本文以Javascript语言出发介绍代码整洁之道,但其中理论同样适用于其他编程语言。

1. 强类型检查

使用===而不是==

0 == false // true 
0 === false // false 
2 == "2" // true 
2 === "2" // false 

// example 
const val = "123"; 
if (val === 123) { 
  console.log(val); 
  // it cannot not be reached 
} 
if (val === "123") { 
  console.log(val); 
  // it can be reached
}

2. 变量命名

将你的变量以能够表述其使用意图的方式进行命名

按照这种方式,这些变量就能够变得可搜索并且使其具有将强的可读性。

不推荐👎

let daysSLV = 10;

let y = new Date().getFullYear();

let ok;

if (user.age > 30) {
  ok = true;
}

推荐👍

const MAX_AGE = 30;

let daysSinceLastVisit = 10;

let currentYear = new Date().getFullYear();

...

const isUserOlderThanAllowed = user.age > MAX_AGE;

不要添加一些额外且不必要的词汇到你的变量名称中。

不推荐👎

let nameValue;

let theProduct;

推荐👍

let name; 

let product;

不要忽略保存变量的上下文信息的需求

不推荐👎

const products = ["T-Shirt", "Shoes", "Watches", "Bags"];

products.forEach(p => {
  doSomething();
  doSomethingElse();
  // ...
  // ...
  // ...
  // ...
  // `p` 表示什么?
  register(p);
});

推荐👍

const products = ["T-Shirt", "Shoes", "Watches", "Bags"];

products.forEach(product => {
  doSomething();
  doSomethingElse();
  // ...
  // ...
  // ...
  register(product);
});

不要在变量中增加不必要的信息

不推荐👎

const product = {
  productId: 1,
  productName: "T-Shirt",
  productPrice: 8.99,
  productUnits: 12
};

...

product.productName;

推荐👍

const product = {
  id: 1,
  name: "T-Shirt",
  price: 8.99,
  units: 12
};

...

product.name;

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

不推荐👎

getUserInfo();

getClientData();

getCustomerRecord();

推荐👍

getProduct();

3. 函数

使用具有描述性的函数名称。

鉴于函数通常代表某种特定的行为,因此函数名应采用一个动词或者短语,其能够充分表达函数背后的意图和参数的含义。

函数名即函数的作用

不推荐👎

function email(user) { 
  // implementation 
}

推荐👍

function sendEmailUser(emailAddress) { 
  // implementation 
}

避免参数数量过多

理想情况下,函数应该包含两个或者更少的参数。参数的数量越少,该函数越容易被测试

不推荐👎

function getProducts(fields, fromDate, toDate) { 
  // implementation 
}

推荐👍

function getProducts({ fields, fromDate, toDate }) {
  // implementation
}

getProducts({
  fields: ['id', 'name', 'price', 'units],
  fromDate: '2020-07-01',
  toDate: '2020-07-22'
});

使用默认参数,而不是条件判断

不推荐👎

function createShape(type) { 
  const shapeType = type || "circle"; 
  // ... 
}

推荐👍

function createShape(type = "circle") { 
  // ... 
}

避免在一个函数体中执行多个操作

不推荐👎

function notifyUsers(users) {
  users.forEach(user => {
    const userRecord = database.lookup(user);
    if (userRecord.isVerified()) {
      notify(user);
    }
  });
}

推荐👍

function notifyVerifiedUsers(users) {
  users.filter(isUserVerified).forEach(notify);
}

function isUserVerified(user) {
  const userRecord = database.lookup(user);
  return userRecord.isVerified();
}

使用Object.assign来设置默认对象

不推荐👎

const shapeConfig = {
  type: "circle",
  width: 150,
  height: null
};

function createShape(config) {
  config.type = config.type || "circle";
  config.width = config.width || 300;
  config.height = config.height || 300;
}

createShape(shapeConfig);

推荐👍

const shapeConfig = {
  type: "circle",
  width: 150
  // Exclude the 'height' key
};

function createShape(config) {
  config = Object.assign(
    {
      type: "circle",
      width: 300,
      height: 300
    },
    config
  );
  ...
}

createShape(shapeConfig);

不要将标志位作为参数,因为其使得函数的作用超出其本身预设的范围

不推荐👎

function createFile(name, isPublic) {
  if (isPublic) {
    fs.create(`./public/${name}`);
  } else {
    fs.create(name);
  }
}

推荐👍

function createFile(name) {
  fs.create(name);
}

function createPublicFile(name) {
  createFile(`./public/${name}`);
}

切忌污染全局变量

如果你想要对已有对象进行拓展,请使用ES6 的Class类和继承语法,而不是在原生对象的原型链上创建拓展函数。

不推荐👎

Array.prototype.myFunction = function myFunction() { 
  // implementation 
};

推荐👍

class SuperArray extends Array { 
  myFunc() { 
    // implementation 
  } 
}

4. 条件判断

避免使用负条件(会使得逻辑复杂化)

不推荐👎

function isPostNotPublished(post) { 
  // implementation 
} 

if (!isPostNotPublished(post)) { 
  // implementation 
}

推荐👍

function isPostPublished(user) {  
  // implementation
}

if (isPostPublished(user)) {  
  // implementation
}

使用简化的条件判断

这条规则似乎看起来微不足道,但却值得一提。在明确某个值不是undefinednull,以及在值为boolean类型时,可使用该规则使代码更简洁。

不推荐👎

if (isValid === true) { 
  // do something... 
} 

if (isValid === false) { 
  // do something... 
}

推荐👍

if (isValid) { 
  // do something... 
} 
if (!isValid) {
  // do something... 
}

在任何情况下避免使用条件判断

使用多态(polymorphism)和继承(inheritance)

不推荐👎

class Dog {
  // ...
  getBreed() {
    switch (this.type) {
      case "GermanShepherd":
        return this.getStandardSize("GermanShepherd");
      case "JackRussellTerrier":
        return this.getStandardSize("JackRussellTerrier");
      case "ShibaInu":
        return this.getStandardSize("ShibaInu");
    }
  }
}

推荐👍

class Dog {
  // ...
}

class GermanShepherd extends Dog {
  // ...
  getStandardSize() {
    return this.standardSize;
  }
}

class JackRussellTerrier extends Dog {
  // ...
  getSize() {
    return this.standardSize;
  }
}

class ShibaInu extends Dog {
  // ...
  getSize() {
    return this.standardSize;
  }
}

5. ES Classes

class类语法是Javascript的新语法糖。除了写法不一样,Class语法能够实现和之前使用原型(prototype)一样的效果,且使得代码更加简明。

不推荐👎

const Product = function(name) {
  if (!(this instanceof Product)) {
    throw new Error("Instantiate Product with `new` keyword");
  }
  this.name = name;
};

Product.prototype.getSize = function getSize() { /**/ };

const Tshirt = function(name, color) {
  if (!(this instanceof Tshirt)) {
    throw new Error("Instantiate Tshirt with `new` keyword");
  }
  Product.call(this, name);
  this.color = color;
};

Tshirt.prototype = Object.create(Product.prototype);
Tshirt.prototype.constructor = Tshirt;
Tshirt.prototype.printColor = function printColor() { /**/ };

推荐👍

class Product {

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

  getDiscount() {
    /* ... */
  }
}

class Tshirt extends Product {

  constructor(name, color) {
    super(name);
    this.color = color;
  }

  getSize() {
    /* ... */
  }
}

使用链式方法

许多受欢迎的库,像jQueryLodash等采用这种模式,其能够使得代码变得不那么冗长。

在你所定义的类中,通过在每个函数结尾返回this,你就可以在之后链入更多类函数。

不推荐👎

class Product {

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

  setPrice(price) {
    this.price = price;
  }

  setUnits(units) {
    this.units = units;
  }

  save() {
    console.log(this.name, this.price, this.units);
  }
}

const product = new Product("Bag");

person.setPrice(23.99);
person.setUnits(12);
person.save();

推荐👍

class Product {

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

  setName(name) {
    this.name = name;
    // Return this for chaining
    return this;
  }

  setPrice(price) {
    this.price = price;
    // Return this for chaining
    return this;
  }

  save() {
    console.log(this.name, this.price, this.units);
    // Return this for chaining
    return this;
  }
}

const product = new Product("T-Shirt")
    .setName("Jeans")
    .setAge(31.99)
    .save();

6. 避免使用eval

eval函数能够将字符串传递给Javascript编译器并将其按Javascript语句进行执行

简而言之,你在运行时传递的任何内容都会像在设计时添加的那样执行。

eval("alert('Hi');");

上述代码在执行时将弹出一个信息框,带有“Hi”

应该尽量避免使用Eval函数,因为它并不安全,其为恶意程序员提供了具有威胁性质的潜在载体。

7. 使用JSLint

JSLint等代码格式校验工具是编写规范代码的好帮手。关于这类工具的使用后续将展开讨论】

8. 整体上避坑

尽可能不要重复自己的工作,且避免未使用的代码和死代码(dead code)。【建议直接删除】

总结

这只是您可以改进代码的一小部分。我认为,这里所说的原则是人们经常不遵循的原则。他们尽力了,但是要保持头脑的力量并始终保持出色的成绩并不容易。也许在项目开始时,代码
就很整洁,但是在截止日期之前,通常会忽略原则,而将其移至“ TODO”或“ REFACTOR”部分。

翻译原文来自:medium.com/javascript-…