资讯专栏INFORMATION COLUMN

[译] 如何写出漂亮的 JavaScript 代码

amuqiao / 448人阅读

摘要:如何提高代码的可读性复用性扩展性。严格遵守这条规则会让你的代码可读性更好,也更容易重构。如果违反这个规则,那么代码会很难被测试或者重用。它让你的代码简洁优雅。

原文:https://github.com/ryanmcderm...  
说明:本文翻译自 github 上的一个项目,非全文搬运,只取部分精华。

如何提高代码的可读性、复用性、扩展性。我们将从以下四个方面讨论:

变量

函数

异步

一、变量 用有意义且常用的单词命名
// Bad:
const yyyymmdstr = moment().format("YYYY/MM/DD");
// Good:
const currentDate = moment().format("YYYY/MM/DD");
保持统一

对同一类型的变量使用相同的命名保持统一:

// Bad:
getUserInfo();
getClientData();
getCustomerRecord();
// Good:
getUser()
每个常量(全大写)都该命名

可以用 ESLint 检测代码中未命名的常量。

// Bad:
// 其他人知道 86400000 的意思吗?
setTimeout( blastOff, 86400000 );
// Good:
const MILLISECOND_IN_A_DAY = 86400000;
setTimeout( blastOff, MILLISECOND_IN_A_DAY );
避免无意义的命名

既然创建了一个 car 对象,就没有必要把它的颜色命名为 carColor。

// Bad:
const car = {
    carMake: "Honda",
    carModel: "Accord",
    carColor: "Blue"
};
function paintCar( car ) {
    car.carColor = "Red";
}
// Good:
const car = {
    make: "Honda",
    model: "Accord",
    color: "Blue"
};
function paintCar( car ) {
    car.color = "Red";
}
传参使用默认值
// Bad:
function createMicrobrewery( name ) {
    const breweryName = name || "Hipster Brew Co.";
    // ...
}
// Good:
function createMicrobrewery( name = "Hipster Brew Co." ) {
    // ...
}
二、函数 函数参数( 最好 2 个或更少 )

如果参数超过两个,建议使用 ES6 的解构语法,不用考虑参数的顺序。

// Bad:
function createMenu( title, body, buttonText, cancellable ) {
    // ...
}
// Good:
function createMenu( { title, body, buttonText, cancellable } ) {
    // ...
}
createMenu({
    title: "Foo",
    body: "Bar",
    buttonText: "Baz",
    cancellable: true
});
一个方法只做一件事情

这是一条在软件工程领域流传久远的规则。严格遵守这条规则会让你的代码可读性更好,也更容易重构。如果违反这个规则,那么代码会很难被测试或者重用。

// Bad:
function emailClients( clients ) {
    clients.forEach( client => {
        const clientRecord = database.lookup( client );
        if ( clientRecord.isActive() ) {
            email( client );
        }
    });
}
// Good:
function emailActiveClients( clients ) {
    clients
        .filter( isActiveClient )
        .forEach( email );
}
function isActiveClient( client ) {
    const clientRecord = database.lookup( client );    
    return clientRecord.isActive();
}
函数名上体现它的作用
// Bad:
function addToDate( date, month ) {
    // ...
}
const date = new Date();
// 很难知道是把什么加到日期中
addToDate( date, 1 );
// Good:
function addMonthToDate( month, date ) {
    // ...
}
const date = new Date();
addMonthToDate( 1, date );
删除重复代码,合并相似函数

很多时候虽然是同一个功能,但由于一两个不同点,让你不得不写两个几乎相同的函数。

// Bad:
function showDeveloperList(developers) {
  developers.forEach((developer) => {
    const expectedSalary = developer.calculateExpectedSalary();
    const experience = developer.getExperience();
    const githubLink = developer.getGithubLink();
    const data = {
      expectedSalary,
      experience,
      githubLink
    };
    render(data);
  });
}
function showManagerList(managers) {
  managers.forEach((manager) => {
    const expectedSalary = manager.calculateExpectedSalary();
    const experience = manager.getExperience();
    const portfolio = manager.getMBAProjects();
    const data = {
      expectedSalary,
      experience,
      portfolio
    };
    render(data);
  });
}
// Good:
function showEmployeeList(employees) {
  employees.forEach(employee => {
    const expectedSalary = employee.calculateExpectedSalary();
    const experience = employee.getExperience();
    const data = {
      expectedSalary,
      experience,
    };
    switch(employee.type) {
      case "develop":
        data.githubLink = employee.getGithubLink();
        break
      case "manager":
        data.portfolio = employee.getMBAProjects();
        break
    }
    render(data);
  })
}
使用 Object.assign 设置默认属性
// Bad:
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);
// Good:
const menuConfig = {
  title: "Order",
  // 不包含 body
  buttonText: "Send",
  cancellable: true
};
function createMenu(config) {
  config = Object.assign({
    title: "Foo",
    body: "Bar",
    buttonText: "Baz",
    cancellable: true
  }, config);
  // config : {title: "Order", body: "Bar", buttonText: "Send", cancellable: true}
  // ...
}
createMenu(menuConfig);
尽量不要写全局方法

在 JavaScript 中,永远不要污染全局,会在生产环境中产生难以预料的 bug。举个例子,比如你在 Array.prototype 上新增一个 diff 方法来判断两个数组的不同。而你同事也打算做类似的事情,不过他的 diff 方法是用来判断两个数组首位元素的不同。很明显你们方法会产生冲突,遇到这类问题我们可以用 ES2015/ES6 的语法来对 Array 进行扩展。

// Bad:
Array.prototype.diff = function diff(comparisonArray) {
  const hash = new Set(comparisonArray);
  return this.filter(elem => !hash.has(elem));
};
// Good:
class SuperArray extends Array {
  diff(comparisonArray) {
    const hash = new Set(comparisonArray);
    return this.filter(elem => !hash.has(elem));        
  }
}
尽量别用“非”条件句
// Bad:
function isDOMNodeNotPresent(node) {
  // ...
}
if (!isDOMNodeNotPresent(node)) {
  // ...
}
// Good:
function isDOMNodePresent(node) {
  // ...
}
if (isDOMNodePresent(node)) {
  // ...
}
不要过度优化

现代浏览器已经在底层做了很多优化,过去的很多优化方案都是无效的,会浪费你的时间。

// Bad:
// 现代浏览器已对此( 缓存 list.length )做了优化。
for (let i = 0, len = list.length; i < len; i++) {
  // ...
}
// Good:
for (let i = 0; i < list.length; i++) {
  // ...
}
删除弃用代码

这里没有实例代码,删除就对了

三、类 使用 ES6 的 class

在 ES6 之前,没有类的语法,只能用构造函数的方式模拟类,可读性非常差。

// Good:
// 动物
class Animal {
  constructor(age) {
    this.age = age
  };
  move() {};
}
// 哺乳动物
class Mammal extends Animal{
  constructor(age, furColor) {
    super(age);
    this.furColor = furColor;
  };
  liveBirth() {};
}
// 人类
class Human extends Mammal{
  constructor(age, furColor, languageSpoken) {
    super(age, furColor);
    this.languageSpoken = languageSpoken;
  };
  speak() {};
}
使用链式调用

这种模式相当有用,可以在很多库中都有使用。它让你的代码简洁优雅。

class Car {
  constructor(make, model, color) {
    this.make = make;
    this.model = model;
    this.color = color;
  }

  setMake(make) {
    this.make = make;
  }

  setModel(model) {
    this.model = model;
  }

  setColor(color) {
    this.color = color;
  }

  save() {
    console.log(this.make, this.model, this.color);
  }
}
// Bad:
const car = new Car("Ford","F-150","red");
car.setColor("pink");
car.save();

// Good: 
class Car {
  constructor(make, model, color) {
    this.make = make;
    this.model = model;
    this.color = color;
  }

  setMake(make) {
    this.make = make;
    // NOTE: Returning this for chaining
    return this;
  }

  setModel(model) {
    this.model = model;
    // NOTE: Returning this for chaining
    return this;
  }

  setColor(color) {
    this.color = color;
    // NOTE: Returning this for chaining
    return this;
  }

  save() {
    console.log(this.make, this.model, this.color);
    // NOTE: Returning this for chaining
    return this;
  }
}

const car = new Car("Ford", "F-150", "red").setColor("pink").save();
四、异步 使用 promise 或者 Async/Await 代替回调
// Bad:
get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin", (requestErr, response) => {
  if (requestErr) {
    console.error(requestErr);
  } else {
    writeFile("article.html", response.body, (writeErr) => {
      if (writeErr) {
        console.error(writeErr);
      } else {
        console.log("File written");
      }
    });
  }
});
// Good:
get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin")
  .then((response) => {
    return writeFile("article.html", response);
  })
  .then(() => {
    console.log("File written");
  })
  .catch((err) => {
    console.error(err);
  });

// perfect:
async function getCleanCodeArticle() {
  try {
    const response = await get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin");
    await writeFile("article.html", response);
    console.log("File written");
  } catch(err) {
    console.error(err);
  }
}
后记

如果你想进【大前端交流群】,关注公众号点击“交流加群”添加机器人自动拉你入群。关注我第一时间接收最新干货。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/105035.html

相关文章

  • Top 15 - Material Design框架和类库()

    摘要:这是一个用于构建响应式应用和网站的前端框架。是基于设计的一套丰富的组件。这是一个对混合式手机应用框架的扩展库。到目前为止它仅大小,而且不依赖于任何第三方的插件,它可以很轻量的被用来创建和应用。 _Material design_是Google开发的,目的是为了统一公司的web端和手机端的产品风格。它是基于很多的原则,比如像合适的动画,响应式,以及颜色和阴影的使用。完整的指南详情请看这里...

    Cristic 评论0 收藏0
  • Top 15 - Material Design框架和类库()

    摘要:这是一个用于构建响应式应用和网站的前端框架。是基于设计的一套丰富的组件。这是一个对混合式手机应用框架的扩展库。到目前为止它仅大小,而且不依赖于任何第三方的插件,它可以很轻量的被用来创建和应用。 _Material design_是Google开发的,目的是为了统一公司的web端和手机端的产品风格。它是基于很多的原则,比如像合适的动画,响应式,以及颜色和阴影的使用。完整的指南详情请看这里...

    phpmatt 评论0 收藏0
  • []148个资源让你成为CSS专家

    摘要:层叠样式表二修订版这是对作出的官方说明。速查表两份表来自一份关于基础特性,一份关于布局。核心第一篇一份来自的基础参考指南简写速查表简写形式参考书使用层叠样式表基础指南,包含使用的好处介绍个方法快速写成高质量的写出高效的一些提示。 迄今为止,我已经收集了100多个精通CSS的资源,它们能让你更好地掌握CSS技巧,使你的布局设计脱颖而出。 CSS3 资源 20个学习CSS3的有用资源 C...

    impig33 评论0 收藏0
  • 2017-08-10 前端日报

    摘要:前端日报精选译用搭建探索生命周期中的匿名递归浏览器端机器智能框架深入理解笔记和属性中文上海线下活动前端工程化架构实践沪江技术沙龙掘金周二放送追加视频知乎专栏第期聊一聊前端自动化测试上双关语来自前端的小段子,你看得懂吗众成翻 2017-08-10 前端日报 精选 [译] 用 Node.js 搭建 API Gateway探索 Service Worker 「生命周期」JavaScript ...

    wupengyu 评论0 收藏0

发表评论

0条评论

amuqiao

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<