JavaScript的 5 个不良编码习惯,如今就改掉吧!

翻译:Jessie
原文连接:连接
javascript

每当阅读 JavaScript 代码的时候,你是否有如下感觉:html

  • 你几乎不明白代码的做用是什么?
  • 代码使用了不少 JavaScript 技巧?
  • 命名和代码风格至关混乱?

上面这些就是典型的不良的编码习惯。java

在这篇文章里,我会描述在 JavaScript 中 5 个常见 不良的编码习惯,最重要的是我会推荐个人可行性建议来去摆脱托这些习惯。git

1. 不要使用隐式类型转换

JavaScript 是弱类型语言, 若是使用正确,这是一个好处,由于它给你提供了灵活性github

大多数操做符+ - * / ==(但不是===)在处理不一样类型的操做数时使用类型的隐式转换数组

语法 if (condition) {...} while(condition) {...} 隐式的将条件转换为布尔值。安全

下面的例子依赖隐式类型转换,我打赌你会感受到困惑:bash

console.log("2" + "1");  // => "21"
console.log("2" - "1");  // => 1

console.log('' == 0);    // => true

console.log(true == []); // -> false
console.log(true == ![]); // -> false
复制代码

过度依赖隐式类型转换是一种坏习惯, 首先,它让你的代码在边界状况下面不那么稳定,其次,你增长了引入难以复制和修复的 bug 的机会。ide

咱们实现一个传入对象类型参数的函数,若是属性不存在,这个函数返回一个默认值:函数

function getProp(object, propertyName, defaultValue) {
  if (!object[propertyName]) {
    return defaultValue;
  }
  return object[propertyName];
}

const hero = {
  name: 'Batman',
  isVillian: false
};

console.log(getProp(hero, 'name', 'Unknown'));     // => 'Batman'
复制代码

getProp 获取 name 属性的值是 Batman

尝试去访问 isVillian 属性会怎么样呢:

console.log(getProp(hero, 'isVillian', true)); // => true
复制代码

这是个错误, 即便 heroisVillian 属性值是 falsegetProp() 函数返回不正确的 true

之因此会发生这种状况,是由于属性存在校验依赖于 if (!object[propertyName]) {...} 隐式转换为布尔值。

这种类型的错误很难发现,去修复这个函数,须要明确验证值的类型:

function getPropFixed(object, propertyName, defaultValue) {
   if (object[propertyName] === undefined) {
     return defaultValue;
   }
   return object[propertyName];
}

const hero = {
  name: 'Batman',
  isVillian: false
};

console.log(getPropFixed(hero, 'isVillian', true)); // => false
复制代码

object[propertyName] === undefined 明确的验证属性访问器是否为 undefined

你知道其余方法去校验属性是否在对象里面?若是是这样,欢迎在下面留言!

边注: 第四节建议避免直接使用 undefined。因此能够用 in 操做符改进上述解决方案。

function getPropFixedBetter(object, propertyName, defaultValue) {
   if (!(propertyName in object)) {
     return defaultValue;
   }
   return object[propertyName];
}
复制代码

下面是个人建议:只要有可能,不要使用隐式类型转换。 相反,确认变量和函数参数始终拥有相同的类型。必要时使用显式类型转换。

最佳实践列表:

  • 始终使用严格相等操做符 === 进行比较
  • 不要使用弱相等操做符 ==
  • 加号操做符 operand1 + operand2: 两边的操做数应该是数值或者字符串类型
  • 算数操做符 - * / % ** :两边操做数应该是数值类型
  • if (condition) {...}, while (condition) {...} 等语法:条件应该是布尔类型

你可能会说这种方法须要编写更多的代码...是的!可是使用显式方法,你控制了你代码的行为。再者,显式提升了可读性。

2. 不要使用旧的 JavaScript 技巧

JavaScript 的有趣之处在于它的做者没料到这种语言会如此流行。

基于 JavaScript 构建应用程序的复杂性比语言发展的速度还要快。这种状况致使开发者使用 JavaScript 技巧和变通方法,只是为了功能实现。

一个典型例子是查询一个数组是否包含一个项,我历来不喜欢使用 array.indexOf(item) !== -1 去验证某一项是否存在。

ECMAScript 2015 及更高版本更强大,你可使用新语法特性来安全重构许多技巧。

重构 array.indexOf(item) !== -1 以支持新的 ES2015 方法 array.includes(item)

按照 my compiled list of refactorings 去移除你 JavaScript 代码中老的技巧。

3. 不要污染函数的做用域

在 ES2015 以前,JavaScript 变量是函数做用域的。所以,您可能养成了将全部变量声明为函数做用域的坏习惯。

咱们来看一个例子:

function someFunc(array) {
  var index, item, length = array.length;
  /*
   * Lots of code
   */
  for (index = 0; index < length; index++) {
    item = array[index];
    // Use `item`
  }
  return someResult;
}
复制代码

变量 index item length 都是函数做用域,可是这些变量污染了函数做用域由于他们只是在 for() 代码块内使用。

根据块级做用域变量 letconst 的介绍,你应该尽可能限制变量的生命周期。

让咱们清理函数做用域:

function someFunc(array) {
  /*
   * Lots of code
   */
  const length = array.length;
  for (let index = 0; index < length; index++) {
    const item = array[index];
    // Use `item`
  }
  return someResult;
}
复制代码

indexitem 变量受限于 for() 循环块范围, length 被移到靠近使用的地方。

重构的代码很容易明白由于变量不会分布在整个函数做用域内。它们放在使用地点附近。

参数定义在使用的区块范围内:

if 块范围

// Bad
let message;
// ...
if (notFound) {
  message = 'Item not found';
  // Use `message`
}
复制代码
// Good
if (notFound) {
  const message = 'Item not found';
  // Use `message`
}
复制代码

for 块范围

// Bad
let item;
for (item of array) {
  // Use `item`
}
复制代码
// Good
for (const item of array) {
  // Use `item`
}
复制代码

4. 尽可能去避免 undefined 和 null

一个变量没赋值会被计算为 undefined。例如:

let count;
console.log(count); // => undefined

const hero = {
  name: 'Batman'
};
console.log(hero.city); // => undefined
复制代码

count 变量被定义,可是没有初始值,JavaScript 隐式赋值为 undefined

当访问不存的属性 hero.city,也是返回 undefined

为何直接使用 undefined 是个坏习惯? 由于当你开始对比 undefined 时,你正在处理未初始化状态的变量。

变量、对象属性、数组在使用前必须有初始化的值!

JavaScript 提供了不少可能性来避免与 undefined 进行比较。

属性是否存在

// Bad
const object = {
  prop: 'value'
};
if (object.nonExistingProp === undefined) {
  // ...
}
复制代码
// Good
const object = {
  prop: 'value'
};
if ('nonExistingProp' in object) {
  // ...
}
复制代码

对象的默认属性

// Bad
function foo(options) {
  if (object.optionalProp1 === undefined) {
    object.optionalProp1 = 'Default value 1';
  }
  // ...
}
复制代码
// Good
function foo(options) {
  const defaultProps = {
    optionalProp1: 'Default value 1'
  };
  options = {
    ...defaultProps,
    ...options
  };
  // ...
}
复制代码

函数默认参数

// Bad
function foo(param1, param2) {
  if (param2 === undefined) {
    param2 = 'Some default value';
  }
  // ...
}
复制代码
// Good
function foo(param1, param2 = 'Some default value') {
  // ...
}
复制代码

null 是一个缺乏对象的指示符。

你应该努力去避免在函数中返回 null, 更重要的是,避免使用 null 参数去访问函数。

只要 null 出如今你的调用堆栈中,你必须在每一个可能访问 null的函数中检验它是否存在, 这会产生错误。

function bar(something) {
  if (something) {
    return foo({ value: 'Some value' });
  } else {
    return foo(null);
  }
}

function foo(options) {
  let value = null;
  if (options !== null) {
    value = options.value;
    // ...
  }
  return value;
}
复制代码

尝试去书写没有涉及 null 的代码。可能的备选方案是 try/catch 机制,默认对象的用法。

ALGOL 的创造者 Tony Hoare ,曾经说过:

“我称之为十亿美圆的错误...[...] 我正在设计第一个用于面向对象语言的引用的全面类型系统。[...]可是我没法抗拒提出空引用的诱惑,很简单由于它很容易实现。 这致使无数错误,漏洞和系统崩溃,在过去40年里,这可能致使数十亿美圆的痛苦和伤害。”

The worst mistake of computer science” 这篇文章深刻解释了为何 null 会对你的代码质量形成伤害。

5. 不要使用随意的代码风格,执行一个标准

还有什么比阅读混乱的编码风格更使人畏惧的呢?你永远不知道会发生什么!

假如代码库包含不一样不少开发者不一样的编码风格会怎么样?各类各样的字符涂鸦墙。

整个团队和应用程序代码库中相同的编码风格是必须的。 它提升了代码的可读性。

有用的编码风格的示例:

但说实话,我很懒。当我在截止日期前,或者在准备回家以前提交时,我可能就“忘记”了优化个人代码。

“我本身很懒”的意思是:保持代码不变,之后更新。可是之后意味着永远不会。

我推荐自动化编码风格验证的过程:

  • 安装 eslint
  • 配置适合你代码风格的 eslint
  • 设置预提交的 hook, 在提交以前运行 eslint 验证

eslint-prettier-husky-boilerplate.开始。

6. 总结

书写高质量、简洁的的代码须要自律,克服不良的编码习惯。

JavaScript 是一个宽容的语言,有不少的灵活性。 可是你要注意你用的功能是什么。 个人建议是避免隐式类型转换并减小undefinednull的使用。

近期 JavaScript 发展十分迅速,识别复杂的代码,而且使用最新的 JavaScript 特性去重构它。

贯穿代码库的一致的编码风格有利于可读性。

良好的编码技一直是一个成功的解决方案。

你知道 JavaScript 中还有哪些很差的编码习惯吗?

相关文章
相关标签/搜索