Airbnb规范

  • 不使用分号
  • 使用分号

为什么? 当 JavaScript 遇到没有分号的换行符时,它使用一组称为自动分号插入的规则来确定是否应该将换行符视为语句的结尾,并且(顾名思义)如果被这样认为的话,在换行符前面自动插入一个分号。ASI(自动分号插入)包含了一些稀奇古怪的的行为,不过,如果 JavaScript 错误地解释了你的换行符,你的代码将会被中断执行。随着新功能成为 JavaScript 的一部分,这些规则将变得更加复杂。明确地结束你的语句并配置你的 linter 来捕获缺少的分号,将有助于防止遇到问题

  • 不使用逗号结尾

  • 使用逗号结尾

  • 使用let,const,摒弃var

const a = [1, 2], const b = a, b[0] = 9, a = ?, b = ?
  • 使用字面量创建Object和Array
// bad
const obj = new Object(), const arr = new Array()

// good
const obj = {1, 2, 3}, const arr = [7, 8, 9]
  • {}前后要空格
 { 1, 2, 3 }
  • []前后无需空格
[1, 2, 3]
  • 注释符号后面要空格
// 注释
  • 逗号之后要空格
const a = [1, 2, 3]
  • 冒号之后要空格
const obj = { abc: 123 }
  • 使用对象方法速记语法
// bad
const atom = {
    value: 1,
 
    addValue: function (value) {
    return atom.value + value;
    },
}

// good
const atom = {
    value: 1,
 
    addValue(value) {
    return atom.value + value;
    },
}
  • 使用表达式创建funtion
// 为什么? 函数声明很容易被提升(Hoisting),你可以在函数被定义之前引用该函数。这对可读性和可维护性来说都是不利的。

// bad
function fn () {}

// good
const fn = () => {}
  • 使用对象属性速记语法
// bad
const obj = { abc: abc }

// good
const obj = { abc }
  • 只用引号引无效标识符的属性
// bad
const bad = {
    'foo': 3,
    'bar': 4,
    'data-blah': 5,
}

// good
const good = {
    foo: 3,
    bar: 4,
    'data-blah': 5,
}
  • 使用展开符…复制数组
// bad
const len = items.length
const itemsCopy = []
let i
 
for (i = 0; i < len; i += 1) {
    itemsCopy[i] = items[i]
}
 
// good
const itemsCopy = [...items]
  • 使用展开操作符 ... 代替 Array.from
const foo = document.querySelectorAll('.foo');
 
// good
const nodes = Array.from(foo);
 
// best
const nodes = [...foo];
  • 在数组方法回调中使用 return 语句。如果函数体由一个返回无副作用的表达式的单个语句组成,那么可以省略返回值
// bad
inbox.filter((msg) => {
    const { subject, author } = msg;
    if (subject === 'Mockingbird') {
    return author === 'Harper Lee'
    } else {
    return false
    }
})
 
// good
inbox.filter((msg) => {
    const { subject, author } = msg;
    if (subject === 'Mockingbird') {
    return author === 'Harper Lee'
    }
 
    return false
})
  • 当访问和使用对象的多个属性时,请使用对象解构
// bad
function getFullName(user) {
    const firstName = user.firstName
    const lastName = user.lastName
 
    return `${firstName} ${lastName}`
}
 
// good
function getFullName(user) {
    const { firstName, lastName } = user
    return `${firstName} ${lastName}`
}
 
// best
function getFullName({ firstName, lastName }) {
    return `${firstName} ${lastName}`
}
  • 使用数组解构
const arr = [1, 2, 3, 4]
 
// bad
const first = arr[0]
const second = arr[1]
 
// good
const [first, second] = arr
  • 字符串使用单引号 ''
// bad
const name = "Capt. Janeway"
 
// bad - 模板字面量应该包含插值或换行符
const name = `Capt. Janeway`
 
// good
const name = 'Capt. Janeway'
  • 以编程方式构建字符串时,请使用模板字符串而不是字符串连接
// bad
function sayHi(name) {
    return 'How are you, ' + name + '?'
}
 
// bad
function sayHi(name) {
    return ['How are you, ', name, '?'].join()
}
 
// bad
function sayHi(name) {
    return `How are you, ${ name }?`
}
 
// good
function sayHi(name) {
    return `How are you, ${name}?`
}
  • 不要使用 arguments,可以选择 rest 语法 ... 替代
//为什么?使用 ... 能明确你要传入的参数。另外 rest(剩余)参数是一个真正的数组,而 arguments 是一个类数组(Array-like)。

// bad
function concatenateAll() {
    const args = Array.prototype.slice.call(arguments);
    return args.join('');
}
 
// good
function concatenateAll(...args) {
    return args.join('');
}
  • 使用默认参数语法,而不要使用一个变化的函数参数
// bad
function handleThings(opts) {
    opts = opts || {};
      // 如果opts的值就是false怎么办?按上面的写法,它永远等于{}了,也就是true
    // ...
}
 
// good
function handleThings(opts = {}) {
    // ...
}
  • 不要改变参数
// 为什么?因为对象是引用类型,操作作为参数传入的对象,可能会在调用原始对象时造成不必要的变量副作用。

// bad
function f1(obj) {
    obj.key = 1
}
 
// good
function f2(obj) {
    const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1
}
  • 参数不要重新赋值
// bad
function f1(a) {
    a = 1
    // ...
}
 
function f2(a) {
    if (!a) { a = 1 }
    // ...
}
 
// good
function f3(a) {
    const b = a || 1
    // ...
}
 
function f4(a = 1) {
    // ...
}
  • 如果你的函数只有一个参数并且不使用大括号,则可以省略参数括号。否则,为了清晰和一致性,总是给参数加上括号
// bad
[1, 2, 3].map((x) => x * x)
 
// good
[1, 2, 3].map(x => x * x)
 
// good
[1, 2, 3].map(number => (
    `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!`
))
 
// bad
[1, 2, 3].map(x => {
    const y = x + 1
    return x * y
})
 
// good
[1, 2, 3].map((x) => {
    const y = x + 1
    return x * y
})
  • 一个地方只在一个路径中 import(导入)
// bad
import foo from 'foo'
// … 其他一些 imports … //
import { named1, named2 } from 'foo'
 
// good
import foo, { named1, named2 } from 'foo'
 
// good
import foo, {
    named1,
    named2,
} from 'foo'
  • 将所有 import 导入放在非导入语句的上面
// bad
import foo from 'foo'
foo.init()
 
import bar from 'bar'
 
// good
import foo from 'foo'
import bar from 'bar'
 
foo.init()
  • 不要使用 iterators(迭代器) 。请使用高阶函数,例如 map() 和 reduce() 等�,而不是像 for-in 或 for-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
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
const increasedByOne = numbers.map(num => num + 1)
  • 使用 const 或 let声明每个变量
const items = getItems()
const goSportsTeam = true
const dragonball = 'z'
  • 避免使用一元递增和递减运算符(++, --)
let a = 1

// bad
a++

// good
a += 1
  • 尽量使用 === 和 !== 而非 == 和 !=

  • 在 case 和 default 子句中,使用大括号来创建包含词法声明的语句块

// 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
 
// 拆分成2个分离的三元表达式
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
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
  • 如果一个 if 块总是执行一个 return 语句,后面的 else 块是不必要的。在 else if 块中的 return,可以分成多个 if 块来 return
// 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
    }
}
  • 在大括号前放置 1 个空格
// bad
function test(){
    console.log('test')
}
 
// good
function test() {
    console.log('test')
}
 
// bad
dog.set('attr',{
    age: '1 year',
    breed: 'Bernese Mountain Dog',
})
 
// good
dog.set('attr', {
    age: '1 year',
    breed: 'Bernese Mountain Dog',
})
  • 在控制语句(if、while 等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格
// bad
if(isJedi) {
    fight ()
}
 
// good
if (isJedi) {
    fight()
}
 
// bad
function fight () {
    console.log ('Swooosh!')
}
 
// good
function fight() {
    console.log('Swooosh!')
}
  • 使用空格把运算符隔开
// bad
const x=y+5
 
// good
const x = y + 5
  • 在文件末尾插入一个空行

  • 在声明语句的开始处就执行强制类型转换

  • 创建字符串

// => this.reviewScore = 9
 
// bad
const totalScore = new String(this.reviewScore) // typeof totalScore 是 "object" 而不是 "string"
 
// bad
const totalScore = this.reviewScore + '' // 调用 this.reviewScore.valueOf()
 
// bad
const totalScore = this.reviewScore.toString() // 不能保证返回一个字符串
 
// good
const totalScore = String(this.reviewScore)
  • 数字: 使用 Number 进行转换,而 parseInt 则始终以基数解析字串
const inputValue = '4'
 
// bad
const val = new Number(inputValue)
 
// bad
const val = +inputValue
 
// bad
const val = inputValue >> 0
 
// bad
const val = parseInt(inputValue)
 
// good
const val = Number(inputValue)
 
// good
const val = parseInt(inputValue, 10)
  • 创建布尔值
const age = 0
 
// bad
const hasAge = new Boolean(age)
 
// good
const hasAge = Boolean(age)
 
// best
const hasAge = !!age
  • 避免使用单字母名称。使你的命名具有描述性

  • 当命名对象,函数和实例时使用驼峰式命名

  • 当命名构造函数或类的时候使用单词首字母大写方式命名

  • 将绑定数据到事件时 (不论是 DOM 事件还是其他像Backbone一类的事件), 传递 hash 而不是原始值。 这将允许后续的贡献者不用查找和更新事件的每一个处理程序就可以给事件添加更多的数据

// bad
$(this).trigger('listingUpdated', listing.id)
 
// ...
 
$(this).on('listingUpdated', (e, listingId) => {
    // do something with listingId
})

// good
$(this).trigger('listingUpdated', { listingId: listing.id })
 
// ...
 
$(this).on('listingUpdated', (e, data) => {
    // do something with data.listingId
})
  • Number.isNaN 代替全局 isNaN
// 为什么?全局的 isNaN 方法会将非数字转换为数字, 任何被转换为 NaN 的东西都会返回 true

// bad
isNaN('1.2') // false
isNaN('1.2.3') // true
 
// good
Number.isNaN('1.2.3') // false
Number.isNaN(Number('1.2.3')) // true
  • Number.isFinite 代替全局 isFinite
// 为什么?全局的 isFinite 方法会将非数字转换为数字, 任何被转换为有限大的数字都会返回 true 


// bad
isFinite('2e3') // true
 
// good
Number.isFinite('2e3') // false
Number.isFinite(parseInt('2e3', 10)) // true
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容