简洁的javascript编码(一)--变量、函数

本文作者:IMWeb jaychen 原文出处:IMWeb社区 未经同意,禁止转载

一、变量

  • 使用语义化的变量名称 Bad
const yyyymmdstr = moment().format('YYYY/MM/DD');

Good

const currentDate = moment().format('YYYY/MM/DD');
  • 使用可搜索的名称 通常我们读代码比写得多,所以代码都可读性以及可搜索性非常重要。如果对变量不负于语义化、易理解的名字,代码的阅读者将非常痛苦。buddy.js以及ESLint能够帮助我们定位未命名的常量。 Bad
// 86400000 是什么鬼?    
setTimeout(blastOff, 86400000);

Good

// 通过大写常量来定义.
const MILLISECONDS_IN_A_DAY = 86400000;
setTimeout(blastOff, MILLISECONDS_IN_A_DAY);
  • 使用说明性的临时变量 Bad
const address = 'One Infinite Loop, Cupertino 95014';
const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
saveCityZipCode(address.match(cityZipCodeRegex)[1], address.match(cityZipCodeRegex)[2]);

Good

const address = 'One Infinite Loop, Cupertino 95014';
const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
const [, city, zipCode] = address.match(cityZipCodeRegex) || [];
saveCityZipCode(city, zipCode);
  • 避免费脑理解的临时变量 在遍历时,避免简单无意义的变量命名 Bad
const locations = ['Austin', 'New York', 'San Francisco'];
locations.forEach((l) => {
  doStuff();
  doSomeOtherStuff();
  // ...
  // ...
  // ...
  // 等等 我忘记了l是啥了?
  dispatch(l);
});

Good

const locations = ['Austin', 'New York', 'San Francisco'];
locations.forEach((location) => {
  doStuff();
  doSomeOtherStuff();
  // ...
  // ...
  // ...
  dispatch(location);
});
  • 不添加不需要的内容 如果你的类名/对象名已经能够表述某些信息,那么在类/对象名的属性中就不需要重复命名。 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(breweryName = 'Hipster Brew Co.') {
  // ...
}

二、函数

  • 函数参数最好不超过2个 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 emailClients(clients) {
  clients
    .filter(isClientActive)
    .forEach(email);
}
function isClientActive(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 parseBetterJSAlternative(code) {
  const REGEXES = [
    // ...
  ];
  const statements = code.split(' ');
  const tokens = [];
  REGEXES.forEach((REGEX) => {
    statements.forEach((statement) => {
      // ...
    });
  });
  const ast = [];
  tokens.forEach((token) => {
    // lex...
  });
  ast.forEach((node) => {
    // parse...
  });
}

Good:

function tokenize(code) {
  const REGEXES = [
    // ...
  ];
  const statements = code.split(' ');
  const tokens = [];
  REGEXES.forEach((REGEX) => {
    statements.forEach((statement) => {
      tokens.push( /* ... */ );
    });
  });
  return tokens;
}
function lexer(tokens) {
  const ast = [];
  tokens.forEach((token) => {
    ast.push( /* ... */ );
  });
  return ast;
}
function parseBetterJSAlternative(code) {
  const tokens = tokenize(code);
  const ast = lexer(tokens);
  ast.forEach((node) => {
    // parse...
  });
}
  • 移除重复的代码 你是否尽可能地在避免重复代码呢?在你将要修改某处逻辑时,重复代码将会非常恶心导致你不止修改一处地方。 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();
    let portfolio = employee.getGithubLink();
    if (employee.type === 'manager') {
      portfolio = employee.getMBAProjects();
    }
    const data = {
      expectedSalary,
      experience,
      portfolio
    };
    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',
  // 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 现在等于: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true}
  // ...
}
createMenu(menuConfig);
  • 避免在函数参数使用flags 在函数使用flags说明你的函数不满足单一职责原则。 Bad:
function createFile(name, temp) {
  if (temp) {
    fs.create(`./temp/${name}`);
  } else {
    fs.create(name);
  }
}

Good:

function createFile(name) {
  fs.create(name);
}
function createTempFile(name) {
  createFile(`./temp/${name}`);
}
  • 避免副作用 如果某个函数除了接收输入值与返回值之外还做了其他事,那么就称其具有副作用。典型的副作用譬如写文件、修改某些全局变量、修改内存参数等等。 在编程中我们不可避免的需要产生副作用,譬如上面例子中我们需要写入到某个外部文件。而你应当做的就是将所有的写文件操作由某个服务统一处理,而不应该将写文件的操作分散到数个类或者函数中。这一点最大的优势在于避免了不同对象之间共享状态。 Bad:
// 定义全局变量
// 如果我们有其他的函数引用了该变量,那么我们就无法预测该变量类型
let name = 'Ryan McDermott';
function splitIntoFirstAndLastName() {
  name = name.split(' ');
}
splitIntoFirstAndLastName();
console.log(name); // ['Ryan', 'McDermott'];

Good:

function splitIntoFirstAndLastName(name) {
  return name.split(' ');
}
const name = 'Ryan McDermott';
const newName = splitIntoFirstAndLastName(name);
console.log(name); // 'Ryan McDermott';
console.log(newName); // ['Ryan', 'McDermott'];

Bad:

const addItemToCart = (cart, item) => {
  cart.push({ item, date: Date.now() });
};

Good:

const addItemToCart = (cart, item) => {
  return [...cart, { item, date : Date.now() }];
};
  • 避免污染全局变量 由于你的修改导致污染全局变量,可能导致另外一个库的使用者在不知情的情况下出现生产环境异常。例如,你想扩展Array方法diff,用于区分2个数组的不同。你可能通过Array的原型链扩展,但可能导致其他库的的diff方法失效,例如找出一个数组第一跟最后一个元素的不同的方法。所以这就是为什么更建议通过ES6的classes,并且使用继承方式去添加新的功能函数。 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:
const programmerOutput = [
  {
    name: 'Uncle Bobby',
    linesOfCode: 500
  }, {
    name: 'Suzie Q',
    linesOfCode: 1500
  }, {
    name: 'Jimmy Gosling',
    linesOfCode: 150
  }, {
    name: 'Gracie Hopper',
    linesOfCode: 1000
  }
];
let totalOutput = 0;
for (let i = 0; i < programmerOutput.length; i++) {
  totalOutput += programmerOutput[i].linesOfCode;
}

Good:

const programmerOutput = [
  {
    name: 'Uncle Bobby',
    linesOfCode: 500
  }, {
    name: 'Suzie Q',
    linesOfCode: 1500
  }, {
    name: 'Jimmy Gosling',
    linesOfCode: 150
  }, {
    name: 'Gracie Hopper',
    linesOfCode: 1000
  }
];
const INITIAL_VALUE = 0;
const totalOutput = programmerOutput
  .map((programmer) => programmer.linesOfCode)
  .reduce((acc, linesOfCode) => acc + linesOfCode, INITIAL_VALUE);
  • 封装条件判断 Bad:
if (fsm.state === 'fetching' && isEmpty(listNode)) {
  // ...
}

Good:

function shouldShowSpinner(fsm, listNode) {
  return fsm.state === 'fetching' && isEmpty(listNode);
}
if (shouldShowSpinner(fsmInstance, listNodeInstance)) {
  // ...
}
  • 避免反义条件函数 Bad:
function isDOMNodeNotPresent(node) {
  // ...
}
if (!isDOMNodeNotPresent(node)) {
  // ...
}

Good:

function isDOMNodePresent(node) {
  // ...
}
if (isDOMNodePresent(node)) {
  // ...
}
  • 避免条件选择 这似乎听起来是一个不可能的任务,没有if条件选择语句的话又该如何编程呢?在这里我们推荐使用多态性来达成这一目标,因为如果在函数或类中嵌入过多的if语句,会导致该函数或者类破坏单一职责原则。 Bad:
class Airplane {
  // ...
  getCruisingAltitude() {
    switch (this.type) {
      case '777':
        return this.getMaxAltitude() - this.getPassengerCount();
      case 'Air Force One':
        return this.getMaxAltitude();
      case 'Cessna':
        return this.getMaxAltitude() - this.getFuelExpenditure();
    }
  }
}

Good:

class Airplane {
  // ...
}
class Boeing777 extends Airplane {
  // ...
  getCruisingAltitude() {
    return this.getMaxAltitude() - this.getPassengerCount();
  }
}
class AirForceOne extends Airplane {
  // ...
  getCruisingAltitude() {
    return this.getMaxAltitude();
  }
}
class Cessna extends Airplane {
  // ...
  getCruisingAltitude() {
    return this.getMaxAltitude() - this.getFuelExpenditure();
  }
}
  • 避免过度优化

现代浏览器已经在运行时做了很多的优化,因此很多时候如果我们要遵循那些流传已久的优化策略不过是浪费时间。可以参考这个来获取建议的优化要点。 Bad:

// On old browsers, each iteration with uncached `list.length` would be costly
// because of `list.length` recomputation. In modern browsers, this is optimized.
// 在老的浏览器,每次循环都会去计算list.lenth造成消耗
// 但在现在浏览器,已经被优化了
for (let i = 0, len = list.length; i < len; i++) {
  // ...
}

Good:

for (let i = 0; i < list.length; i++) {
  // ...
}

原文连接

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Crossin的编程教室

这些年,你们一起踩过的坑(2)

上次我们踩坑总结文章 这些年,你们一起踩过的坑(1) 受到了不少同学的认可。我也确信文中所涉及的问题是非常具有普遍性的,对绝大多数初学者都会有帮助。

1163
来自专栏编程之旅

数据结构——无权图的路径问题(C++和java实现)

好像又是接近半个月没有更新,这半个月忙着结婚的各项事情,本来预计的学习任务也拖拖拉拉,进度缓慢。吐槽一句,拍婚纱照真的是最非常非常累的一件事情,不想再有下次了。

1142
来自专栏Spark学习技巧

Flink DataStream编程指南

Flink程序是执行分布式集合转换(例如,filtering, mapping, updating state, joining, grouping, defi...

1.9K7
来自专栏行者悟空

我眼中的并发编程——Fork/Join模型

1945
来自专栏Crossin的编程教室

真值表

逻辑判断是编程中极为常用的知识。之前的课我们已经说过,见第6课和第11课。但鉴于逻辑运算的重要性,今天我再把常用的运算结果总结一下,供大家参考。 这种被称为“真...

2364
来自专栏tkokof 的技术,小趣及杂念

C++11(14) 简易推荐小记~

  容器内元素操作是个很普通的需求,工作中应是屡见不鲜,这里假设有个list容器,存储的是一系列int,表达的意思就算作是年龄吧,新年将近,大家的年龄也都会不情...

902
来自专栏

shell之sort命令

1 sort的工作原理 sort将文件的每一行作为一个单位,相互比较,比较原则是从首字符向后,依次按ASCII码值进行比较,最后将他们按升序输出。 [rocro...

2107
来自专栏九彩拼盘的叨叨叨

如何给函数取个合适的名字

Quora 和 Ubuntu Forums thread 上的 4500 个程序员对上面的问题进行投票。49%的程序员认为给函数,变量等命名是最难的任务。

712
来自专栏chenjx85的技术专栏

leetcode-137-Single Number II-第二种解法

3.9K12
来自专栏Java爬坑系列

【JAVA零基础入门系列】Day3 Java基本数据类型

  前两篇已经将开发环境搭建完成,如果你已经按之前的教程按部就班的完成了部署,那么世界上最优秀的编程语言之一和世界上最优秀的IDE之一已经出现在你的电脑上(此处...

2188

扫码关注云+社区

领取腾讯云代金券