Airbnb JavaScript Style 阅读注解 中

简介: Airbnb JavaScript Style 阅读注解 中

➰arrow Function

  • 箭头函数表达式的语法比函数表达式更短,并且不绑定自己的this,arguments,super或 new.target。这些函数表达式最适合用于非方法函数,并且它们不能用作构造函数
  • const 函数名 = (参数...) => {函数声明}||表达式
  • 执行体为函数声明时需要加上 {},参数的规则参看上文内容
//支持解构函数
const f = ([a,b]=[1,2],{c:c}={c:3})=>a+b+c;
f(); 👉 6;

Classes & Constructors(类与构造函数)

  • 避免直接使用 prototype , 多用 class。因为 class语法更加简洁和且阅读性更棒
// bad
function Queue(contents = []) {
  this.queue = [...contents];
}
Queue.prototype.pop = function () {
  const value = this.queue[0];
  this.queue.splice(0, 1);
  return value;
};
// good
class Queue {
  constructor(contents = []) {
    this.queue = [...contents];
  }
  pop() {
    const value = this.queue[0];
    this.queue.splice(0, 1);
    return value;
  }
}
  • 使用 extends 实现继承,因为这是继承原型的内置功能
// bad
const inherits = require('inherits');
function PeekableQueue(contents) {
  Queue.apply(this, contents);
}
inherits(PeekableQueue, Queue);
PeekableQueue.prototype.peek = function () {
  return this.queue[0];
};
// good
class PeekableQueue extends Queue {
  peek() {
    return this.queue[0];
  }
}
  • 方法可以通过返回 this 来优化方法链
// bad
Jedi.prototype.jump = function () {
  this.jumping = true;
  return true;
};
Jedi.prototype.setHeight = function (height) {
  this.height = height;
};
const luke = new Jedi();
luke.jump(); // => true
luke.setHeight(20); // => undefined
// good
class Jedi {
  jump() {
    this.jumping = true;
    return this;
  }
  setHeight(height) {
    this.height = height;
    return this;
  }
}
const luke = new Jedi();
luke.jump()
luke.setHeight(20);
  • 写一个通用的 toString() 方法也没问题,但是需要保证其能执行且没有其他影响
class Jedi {
  constructor(options = {}) {
    this.name = options.name || 'no name';
  }
  getName() {
    return this.name;
  }
  toString() {
    return `Jedi - ${this.getName()}`;
  }
}
  • 如果没有指定类,那么类需要有一个默认的构造方法。一个空的构造函数或者只是委托给父类是没有必要的
// bad
class Jedi {
  constructor() {}
  getName() {
    return this.name;
  }
}
// bad
class Rey extends Jedi {
  constructor(...args) {
    super(...args);
  }
}
// good
class Rey extends Jedi {
  constructor(...args) {
    super(...args);
    this.name = 'Rey';
  }
}
  • 避免出现两个一样的类成员,因为前一个成员会被覆盖从而导致错误
// bad
class Foo {
  bar() { return 1; }
  bar() { return 2; }
}
// good
class Foo {
  bar() { return 1; }
}
// good
class Foo {
  bar() { return 2; }
}

Modules(模块)

  • 始终使用模块(import/export)来代替非标准的模块系统。你可以选择你喜欢的模块系统,因为模块代表未来
// bad
const AirbnbStyleGuide = require('./AirbnbStyleGuide');
module.exports = AirbnbStyleGuide.es6;
// ok
import AirbnbStyleGuide from './AirbnbStyleGuide';
export default AirbnbStyleGuide.es6;
// best
import { es6 } from './AirbnbStyleGuide';
export default es6;
  • 不要使用通配符进行导出,从而保证你输出一个独立的导出
// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide';
// good
import AirbnbStyleGuide from './AirbnbStyleGuide';
  • 不要把导入和导出写在一起,虽然一行简明扼要,但是我们更需要明确的导入方式和导出方式,保持其一致性
// bad
// filename es6.js
export { es6 as default } from './AirbnbStyleGuide';
// good
// filename es6.js
import { es6 } from './AirbnbStyleGuide';
export default es6;
  • 一个路径一次支持一个导入,因为一个路径一次支持有多个导入,会使代码变得难以维护
// bad
import foo from 'foo';
// … some other imports … //
import { named1, named2 } from 'foo';
// good
import foo, { named1, named2 } from 'foo';
// good
import foo, {
  named1,
  named2,
} from 'foo';
  • 拒绝导出可变绑定,这种方式通常应该避免,但是不排除有某些特殊情况需要这么做,但是应该记住,通常只导出常量引用
// bad
let foo = 3;
export { foo };
// good
const foo = 3;
export { foo };
  • 在具有单一导出的模块中,建议使用默认导出而不是命名导出,这样对于代码的可读性和可维护性更加友好
// bad
export function foo() {}
// good
export default function foo() {}
  • 把所有的导入语句放在一起
// bad
import foo from 'foo';
foo.init();
import bar from 'bar';
// good
import foo from 'foo';
import bar from 'bar';
foo.init();
  • 多行导入应该项多行数组和对象一样缩进,这样保持 {} 内容的一致性
// bad
import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path';
// good
import {
  longNameA,
  longNameB,
  longNameC,
  longNameD,
  longNameE,
} from 'path';
  • 导出语句中不允许出现 webpack 加载器语法。因为导入中使用加载器语法会将代码耦合到模块打包器中,,更建议使用 webpack.config.js
// bad
import fooSass from 'css!sass!foo.scss';
import barCss from 'style!css!bar.css';
// good
import fooSass from 'foo.scss';
import barCss from 'bar.css';

Iterators and Generators(迭代器和发生器)

  • 不要使用迭代器,更推荐使用javascript的高阶方法而不是 for-infor-of 这些。使用 map()every()filter()find()findIndex()reduce()some() 等遍历数组,以及Object.keys()Object.values()Object.entries()去生成数组,以便迭代对象。因为处理返回值的纯函数更容易定位问题
const numbers = [1, 2, 3, 4, 5];
// bad
let sum = 0;
for (let num of numbers) {
  sum += num;
}
sum === 15;
// good
let sum = 0;
numbers.forEach((num) => {
  sum += num;
});
sum === 15;
// best (use the functional force)
const sum = numbers.reduce((total, num) => total + num, 0);
sum === 15;
// bad
const increasedByOne = [];
for (let i = 0; i < numbers.length; i++) {
  increasedByOne.push(numbers[i] + 1);
}
// good
const increasedByOne = [];
numbers.forEach((num) => {
  increasedByOne.push(num + 1);
});
// best (keeping it functional)
const increasedByOne = numbers.map(num => num + 1);
  • 不要使用发生器,因为他们还没有很好的兼容
  • 如果你一定要用发生器,一定要注意关键字符的间距,举个例子,function* 是一个不同于 function 的独特构造,并且 *是其构造的一部分
// bad
function * foo() {
  // ...
}
// bad
const bar = function * () {
  // ...
};
// bad
const baz = function *() {
  // ...
};
// bad
const quux = function*() {
  // ...
};
// bad
function*foo() {
  // ...
}
// bad
function *foo() {
  // ...
}
// very bad
function*
foo() {
  // ...
}
// very bad
const wat = function*
() {
  // ...
};
// good
function* foo() {
  // ...
}
// good
const foo = function* () {
  // ...
};

Properties(属性)

  • 通过常量访问属性的时候使用 .
const luke = {
  jedi: true,
  age: 28,
};
// bad
const isJedi = luke['jedi'];
// good
const isJedi = luke.jedi;
  • 通过变量访问属性的时候用 []
const luke = {
  jedi: true,
  age: 28,
};
function getProp(prop) {
  return luke[prop];
}
const isJedi = getProp('jedi');
  • 使用 ** 进行指数运算
// bad
const binary = Math.pow(2, 10);
// good
const binary = 2 ** 10;

Variables(变量)

  • 总是使用 const 或者 let 来声明变量,这样做可以避免污染全局命名空间
// bad
superPower = new SuperPower();
// good
const superPower = new SuperPower();
  • 每个变量声明都对应一个 const 或者 let。这样做,可以独立的声明每一个变量,而不需要考虑 ;,的关系,同时也方便对每个声明进行调试,而不是跳过所有的声明
// bad
const items = getItems(),
    goSportsTeam = true,
    dragonball = 'z';
// bad
// (compare to above, and try to spot the mistake)
const items = getItems(),
    goSportsTeam = true;
    dragonball = 'z';
// good
const items = getItems();
const goSportsTeam = true;
const dragonball = 'z';
  • letconst 进行分组,这样增强代码可读性
// bad
let i, len, dragonball,
    items = getItems(),
    goSportsTeam = true;
// bad
let i;
const items = getItems();
let dragonball;
const goSportsTeam = true;
let len;
// good
const goSportsTeam = true;
const items = getItems();
let dragonball;
let i;
let length;
  • 在需要的地方声明变量,因为 constlet 是块作用域而不是函数作用域
// bad - unnecessary function call
function checkName(hasName) {
  const name = getName();
  if (hasName === 'test') {
    return false;
  }
  if (name === 'test') {
    this.setName('');
    return false;
  }
  return name;
}
// good
function checkName(hasName) {
  if (hasName === 'test') {
    return false;
  }
  const name = getName();
  if (name === 'test') {
    this.setName('');
    return false;
  }
  return name;
}
  • 不要进行链式声明变量的操作,这样可能创建隐式的全局变量
// bad
(function example() {
  // JavaScript interprets this as
  // let a = ( b = ( c = 1 ) );
  // The let keyword only applies to variable a; variables b and c become
  // global variables.
  let a = b = c = 1;
}());
console.log(a); // throws ReferenceError
console.log(b); // 1
console.log(c); // 1
// good
(function example() {
  let a = 1;
  let b = a;
  let c = a;
}());
console.log(a); // throws ReferenceError
console.log(b); // throws ReferenceError
console.log(c); // throws ReferenceError
// the same applies for `const`

不要使用一元递增和递减操作符(++,--),因为一元递增和一元递减可能受到分号插入的影响,并且可能导致应用中的值递增或者递减,并且不会报错。使用 num += 1 类似的语句也更加有表现力,并且可以避免预先递增或者递减从而导致程序发生意外

// bad
const array = [1, 2, 3];
let num = 1;
num++;
--num;
let sum = 0;
let truthyCount = 0;
for (let i = 0; i < array.length; i++) {
  let value = array[i];
  sum += value;
  if (value) {
    truthyCount++;
  }
}
// good
const array = [1, 2, 3];
let num = 1;
num += 1;
num -= 1;
const sum = array.reduce((a, b) => a + b, 0);
const truthyCount = array.filter(Boolean).length;
```                          

Hoisting(变量提升)

  • var 声明被置于函数作用域的顶部,但是他们的赋值不是, constlet声明会被置于一个新概念TDZ内。因此, typeof() 方法不再安全
// we know this wouldn’t work (assuming there
// is no notDefined global variable)
function example() {
  console.log(notDefined); // => throws a ReferenceError
}
// creating a variable declaration after you
// reference the variable will work due to
// variable hoisting. Note: the assignment
// value of `true` is not hoisted.
function example() {
  console.log(declaredButNotAssigned); // => undefined
  var declaredButNotAssigned = true;
}
// the interpreter is hoisting the variable
// declaration to the top of the scope,
// which means our example could be rewritten as:
function example() {
  let declaredButNotAssigned;
  console.log(declaredButNotAssigned); // => undefined
  declaredButNotAssigned = true;
}
// using const and let
function example() {
  console.log(declaredButNotAssigned); // => throws a ReferenceError
  console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
  const declaredButNotAssigned = true;
}
  • 匿名函数表达式会提升变量名,而不是函数赋值
function example() {
  console.log(anonymous); // => undefined
  anonymous(); // => TypeError anonymous is not a function
  var anonymous = function () {
    console.log('anonymous function expression');
  };
}
  • 命名函数表达式提升变量名,而不是函数名或者函数体
function example() {
  console.log(named); // => undefined
  named(); // => TypeError named is not a function
  superPower(); // => ReferenceError superPower is not defined
  var named = function superPower() {
    console.log('Flying');
  };
}
// the same is true when the function name
// is the same as the variable name.
function example() {
  console.log(named); // => undefined
  named(); // => TypeError named is not a function
  var named = function named() {
    console.log('named');
  };
}
  • 函数声明提升其名字和函数体
function example() {
  superPower(); // => Flying
  function superPower() {
    console.log('Flying');
  }
}

Comparison Operators & Equality(比较操作符和等号)

  • 使用 ===,!== 取代 ==,!=
  • 条件语句比如if会强制使用ToBoolean抽象方法来进行转换,并且遵循以下规则:
  • Objects 转换为 true
  • Undefined 转换为 false
  • Null 转换为 false
  • Booleans 转换为 the value of the boolean
  • Numbers 转换为 false 如果是 +0, -0, or NaN, 其余为 true
  • Strings 转换为 false 如果是空字符串 '', 其余为 true
if ([0] && []) {
  // true
  // an array (even an empty one) is an object, objects will evaluate to true
}
  • 使用布尔值的快捷比较方式,但是显示比较字符串和数字
// bad
if (isValid === true) {
  // ...
}
// good
if (isValid) {
  // ...
}
// bad
if (name) {
  // ...
}
// good
if (name !== '') {
  // ...
}
// bad
if (collection.length) {
  // ...
}
// good
if (collection.length > 0) {
  // ...
}
  • switch 语句中的 casedefault 使用 {} 来创建块,比如let, const, function, class 也是如此。因为在整个 switch 块中词法声明是随处可见的,但是只有在赋值时才会被初始化,且只有 case 值达到时才会发生。但是当多个 case 子句试图定义相同的东西时,就会发生问题
// bad
switch (foo) {
  case 1:
    let x = 1;
    break;
  case 2:
    const y = 2;
    break;
  case 3:
    function f() {
      // ...
    }
    break;
  default:
    class C {}
}
// good
switch (foo) {
  case 1: {
    let x = 1;
    break;
  }
  case 2: {
    const y = 2;
    break;
  }
  case 3: {
    function f() {
      // ...
    }
    break;
  }
  case 4:
    bar();
    break;
  default: {
    class C {}
  }
}
  • 三元表达式不应该嵌套,而应该单行表达
// bad
const foo = maybe1 > maybe2
  ? "bar"
  : value1 > value2 ? "baz" : null;
// split into 2 separated ternary expressions
const maybeNull = value1 > value2 ? 'baz' : null;
// better
const foo = maybe1 > maybe2
  ? 'bar'
  : maybeNull;
// best
const foo = maybe1 > maybe2 ? 'bar' : maybeNull;
  • 没事不要随便用三元表达式
// bad
const foo = a ? a : b;
const bar = c ? true : false;
const baz = c ? false : true;
// good
const foo = a || b;
const bar = !!c;
const baz = !c;
  • 当多个运算符混在一个语句中时,将需要的运算符括在括号里面,并且用括号区分开 **,%+,-,*,/,这样代码更加有可读性,并且澄清了开发者的意图
// bad
const foo = a && b < 0 || c > 0 || d + 1 === 0;
// bad
const bar = a ** b - 5 % d;
// bad
// one may be confused into thinking (a || b) && c
if (a || b && c) {
  return d;
}
// good
const foo = (a && b < 0) || c > 0 || (d + 1 === 0);
// good
const bar = (a ** b) - (5 % d);
// good
if (a || (b && c)) {
  return d;
}
// good
const bar = a + b / c * d;

Blocks(块)

  • 所有的多行块都要用 {}
// bad
if (test)
  return false;
// good
if (test) return false;
// good
if (test) {
  return false;
}
// bad
function foo() { return false; }
// good
function bar() {
  return false;
}
  • 如果使用 if else, else 需要和 if} 在同一行
// bad
if (test) {
  thing1();
  thing2();
}
else {
  thing3();
}
// good
if (test) {
  thing1();
  thing2();
} else {
  thing3();
}
  • 如果一个 if else 语句内每个代码块都用了 return 语句,那么 else 语句就没有必要,分成多个 if 语句就行了
// bad
function foo() {
  if (x) {
    return x;
  } else {
    return y;
  }
}
// bad
function cats() {
  if (x) {
    return x;
  } else if (y) {
    return y;
  }
}
// bad
function dogs() {
  if (x) {
    return x;
  } else {
    if (y) {
      return y;
    }
  }
}
// good
function foo() {
  if (x) {
    return x;
  }
  return y;
}
// good
function cats() {
  if (x) {
    return x;
  }
  if (y) {
    return y;
  }
}
//good
function dogs(x) {
  if (x) {
    if (z) {
      return y;
    }
  } else {
    return z;
  }
}

Control Statements(控制语句)

  • 如果你的控制语句,比如 if,while等很长,或者超过了行宽,你可以对其中的内容进行换行,但是需要注意,逻辑运算符需要放在行首
// bad
if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) {
  thing1();
}
// bad
if (foo === 123 &&
  bar === 'abc') {
  thing1();
}
// bad
if (foo === 123
  && bar === 'abc') {
  thing1();
}
// bad
if (
  foo === 123 &&
  bar === 'abc'
) {
  thing1();
}
// good
if (
  foo === 123
  && bar === 'abc'
) {
  thing1();
}
// good
if (
  (foo === 123 || bar === "abc")
  && doesItLookGoodWhenItBecomesThatLong()
  && isThisReallyHappening()
) {
  thing1();
}
// good
if (foo === 123 && bar === 'abc') {
  thing1();
}

Comments(注释)

多行注释使用 /** ... */

// bad
// make() returns a new element
// based on the passed in tag name
//
// @param {String} tag
// @return {Element} element
function make(tag) {
  // ...
  return element;
}
// good
/**
 * make() returns a new element
 * based on the passed-in tag name
 */
    function make(tag) {
  // ...
  return element;
}
  • 单行注释用 //,并且在注释内容的上一行,在注释语句之前要空一行,当然,如果注释在文件的第一行就不需要空行了
// bad
const active = true;  // is current tab
// good
// is current tab
const active = true;
// bad
function getType() {
  console.log('fetching type...');
  // set the default type to 'no type'
  const type = this.type || 'no type';
  return type;
}
// good
function getType() {
  console.log('fetching type...');
  // set the default type to 'no type'
  const type = this.type || 'no type';
  return type;
}
// also good
function getType() {
  // set the default type to 'no type'
  const type = this.type || 'no type';
  return type;
}

注释文字以空格作为开始,方便阅读

// bad
//is current tab
const active = true;
// good
// is current tab
const active = true;
// bad
/**
 *make() returns a new element
 *based on the passed-in tag name
 */
function make(tag) {
  // ...
  return element;
}
// good
/**
 * make() returns a new element
 * based on the passed-in tag name
 */
function make(tag) {
  // ...
  return element;
}
 - 为你的提交或者评论加上 `FIXME` 或者 `TODO` 的前缀,好让其他开发者迅速明白你的意思。 `FIXME`表示这个问题需要弄清楚,`TODO`表示这个问题需要解决
 - 使用 `// FIXME` 去注释问题
class Calculator extends Abacus {
  constructor() {
    super();
    // FIXME: shouldn’t use a global here
    total = 0;
  }
}
```
  • 使用 // TODO 去注释问题的解决方法
class Calculator extends Abacus {
  constructor() {
    super();
    // TODO: total should be configurable by an options param
    this.total = 0;
  }
}


目录
相关文章
|
2天前
|
JavaScript 前端开发
|
6月前
|
JavaScript
js改变style中的值
js改变style中的值
|
10月前
|
设计模式 人工智能 前端开发
《现代Javascript高级教程》电子书抢先阅读,了解如何在JavaScript中构建高质量的应用程序!
本书旨在帮助学习者进阶JavaScript编程,涵盖现代JavaScript的高级概念和技术,包括异步编程、函数式编程、模块化开发、ES6+语法等。通过实际项目示例和练习,学习者将深入了解如何在JavaScript中构建高质量的应用程序。
《现代Javascript高级教程》电子书抢先阅读,了解如何在JavaScript中构建高质量的应用程序!
|
缓存 JavaScript 前端开发
Airbnb JavaScript Style 阅读注解 下
Airbnb JavaScript Style 阅读注解 下
120 0
|
JavaScript 前端开发 安全
Airbnb JavaScript Style 阅读注解 上
Airbnb JavaScript Style 阅读注解 上
92 0
|
存储 JavaScript 安全
《JavaScript 面向对象精要》 阅读摘要(下)
高程面向对象这块内容介绍的比较浅显,个人觉得这本小书是高程的补充,看完之后觉得收获匪浅,所以做了个笔记,以备后询 Js中两种基本数据类型:原始类型(基本数据类型)和引用类型; 原始类型保存为简单数据值,引用类型则保存为对象,其本质是指向内存位置的应用。 其它编程语言用栈存储原始类型,用堆存储引用类型,而js则不同:它使用一个变量对象追踪变量的生存期。原始值被直接保存在变量对象里,而引用值则作为一个指针保存在变量对象内,该指针指向实际对象在内存中的存储位置。
《JavaScript 面向对象精要》 阅读摘要(下)
|
存储 Web App开发 JavaScript
《JavaScript 面向对象精要》 阅读摘要(上)
高程面向对象这块内容介绍的比较浅显,个人觉得这本小书是高程的补充,看完之后觉得收获匪浅,所以做了个笔记,以备后询 原始类型和引用类型Js中两种基本数据类型:原始类型(基本数据类型)和引用类型; 原始类型保存为简单数据值,引用类型则保存为对象,其本质是指向内存位置的应用。 其它编程语言用栈存储原始类型,用堆存储引用类型,而js则不同:它使用一个变量对象追踪变量的生存期。原始值被直接保存在变量对象里,而引用值则作为一个指针保存在变量对象内,该指针指向实际对象在内存中的存储位置。
《JavaScript 面向对象精要》 阅读摘要(上)
|
自然语言处理 JavaScript 前端开发
《你不知道的JavaScript》 (上) 阅读摘要
本书属于基础类书籍,会有比较多的基础知识,所以这里仅记录平常不怎么容易注意到的知识点,不会全记,供大家和自己翻阅; 上中下三本的读书笔记: 《你不知道的JavaScript》 (上) 读书笔记 《你不知道的JavaScript》 (中) 读书笔记 《你不知道的JavaScript》 (下) 读书笔记 如果希望获取本书的 PDF 资源,可以关注文末二维码加微信群找群主要~
|
Web App开发 XML 移动开发
JavaScript特性(attribute)、属性(property)和样式(style)
最近在研读一本巨著《JavaScript忍者秘籍》,里面有一篇文章提到了这3个概念。 书中的源码可以在此下载。我将源码放到了线上,如果不想下载,可以直接访问在线网址,修改页面名就能访问到相应示例代码。
JavaScript特性(attribute)、属性(property)和样式(style)
|
JavaScript 前端开发
「注解」《你不知道的JavaScript(上卷)》第三章:函数作用域和块作用域
「注解」《你不知道的JavaScript(上卷)》第三章:函数作用域和块作用域
94 0
「注解」《你不知道的JavaScript(上卷)》第三章:函数作用域和块作用域