diff --git a/languages/javascript.md b/languages/javascript.md index 45635a2..c0dde0a 100644 --- a/languages/javascript.md +++ b/languages/javascript.md @@ -8,39 +8,40 @@ JavaScript速查表 ## 目录 + - [基础知识](#基础知识) + - [类型](#类型) + - [引用](#引用) + - [对象](#对象) + - [数组](#数组) + - [解构](#解构) + - [字符串](#字符串) + - [变量](#变量) + - [属性](#属性) + - [公共约束](#公共约束) + - [注释](#注释) + - [分号](#分号) + - [命名规范](#命名规范) + - [类与函数](#类与函数) + - [函数](#函数) + - [箭头函数](#箭头函数) + - [类与构造函数](#类与构造函数) + - [模块](#模块) + - [迭代器与生成器](#迭代器与生成器) + - [提升](#提升) + - [比较运算符与相等](#比较运算符与相等) + - [事件](#事件) + - [类型转换与强制转换](#类型转换与强制转换) + - [标准库](#标准库) + - [测试](#测试) -- [JavaScript速查表](#javascript速查表) - - [目录](#目录) - - [类型](#类型) - - [引用](#引用) - - [对象](#对象) - - [数组](#数组) - - [解构](#解构) - - [字符串](#字符串) - - [函数](#函数) - - [箭头函数](#箭头函数) - - [类与构造函数](#类与构造函数) - - [模块](#模块) - - [迭代器与生成器](#迭代器与生成器) - - [属性](#属性) - - [变量](#变量) - - [提升](#提升) - - [比较运算符与相等](#比较运算符与相等) - - [注释](#注释) - - [分号](#分号) - - [类型转换与强制转换](#类型转换与强制转换) - - [命名规范](#命名规范) - - [事件](#事件) - - [标准库](#标准库) - - [测试](#测试) +## 基础知识 - -## 类型 +### 类型 - 基本类型 **最新的 ECMAScript 标准定义了 8 种数据类型,分别是** @@ -70,7 +71,7 @@ Object:typeof instance === "object" -## 引用 +### 引用 推荐常量赋值都使用`const`, 值可能会发生改变的变量赋值都使用 `let`。 @@ -94,7 +95,7 @@ if (true) { -## 对象 +### 对象 - 使用字面语法创建对象: @@ -211,7 +212,7 @@ if (true) { -## 数组 +### 数组 - 用扩展运算符做数组浅拷贝,类似上面的对象浅拷贝: @@ -279,7 +280,7 @@ if (true) { -## 解构 +### 解构 - 用对象的解构赋值来获取和使用对象某个或多个属性值: @@ -345,7 +346,7 @@ if (true) { -## 字符串 +### 字符串 - 当需要动态生成字符串时,使用模板字符串而不是字符串拼接: @@ -372,232 +373,550 @@ if (true) { - 永远不要使用 `eval()`,该方法有太多漏洞。 +### 变量 +- 不要使用链式变量赋值 +> 因为会产生隐式的全局变量 -## 函数 +```javascript +// 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 - > 为什么?这是因为函数声明会发生提升,这意味着在一个文件里函数很容易在其被定义之前就被引用了。这样伤害了代码可读性和可维护性。如果你发现一个函数又大又复杂,且这个函数妨碍了这个文件其他部分的理解性,你应当单独把这个函数提取成一个单独的模块。不管这个名字是不是由一个确定的变量推断出来的,别忘了给表达式清晰的命名(这在现代浏览器和类似 babel 编译器中很常见)。这消除了由匿名函数在错误调用栈产生的所有假设。 ([讨论](https://github.com/airbnb/javascript/issues/794)) +// good +(function example() { + let a = 1; + let b = a; + let c = a; +}()); - ```javascript - // bad - function foo() { - // ... - } - - // bad - const foo = function () { - // ... - }; - - // good - // lexical name distinguished from the variable-referenced invocation(s) - // 函数表达式名和声明的函数名是不一样的 - const short = function longUniqueMoreDescriptiveLexicalFoo() { - // ... - }; - ``` +console.log(a); // throws ReferenceError +console.log(b); // throws ReferenceError +console.log(c); // throws ReferenceError - +// the same applies for `const` +``` -- 把立即执行函数包裹在圆括号里: - > 立即执行函数:Immediately Invoked Function expression = IIFE。 为什么?因为这样使代码读起来更清晰(译者注:我咋不觉得)。 另外,在模块化世界里,你几乎用不着 IIFE。 - ```javascript - // immediately-invoked function expression (IIFE) - ( ()=> { - console.log('Welcome to the Internet. Please follow me.'); - }() ); - ``` +- 不要使用一元自增自减运算符(`++`, `--`) -- 不要用 `arguments` 命名参数。他的优先级高于每个函数作用域自带的 `arguments` 对象,这会导致函数自带的 `arguments` 值被覆盖: + > 根据 eslint 文档,一元增量和减量语句受到自动分号插入的影响,并且可能会导致应用程序中的值递增或递减的静默错误。 使用 `num + = 1` 而不是 `num ++` 或 `num ++` 语句也是含义清晰的。 - ```javascript +```javascript // bad - function foo(name, options, arguments) { - // ... - } - - // good - function foo(name, options, args) { - // ... - } - ``` -- 用默认参数语法而不是在函数里对参数重新赋值 + const array = [1, 2, 3]; + let num = 1; + num++; + --num; - ```javascript - // really bad - function handleThings(opts) { - // 如果 opts 的值为 false, 它会被赋值为 {} - // 虽然你想这么写,但是这个会带来一些微妙的 bug。 - opts = opts || {}; - // ... - } - - // still bad - function handleThings(opts) { - if (opts === void 0) { - opts = {}; + let sum = 0; + let truthyCount = 0; + for (let i = 0; i < array.length; i++) { + let value = array[i]; + sum += value; + if (value) { + truthyCount++; } - // ... } - + // good - function handleThings(opts = {}) { - // ... - } - ``` -- 把默认参数赋值放在最后面 + const array = [1, 2, 3]; + let num = 1; + num += 1; + num -= 1; - ```javascript - // bad - function handleThings(opts = {}, name) { - // ... - } - - // good - function handleThings(name, opts = {}) { - // ... - } - ``` + const sum = array.reduce((a, b) => a + b, 0); + const truthyCount = array.filter(Boolean).length; -- 不要修改参数,也不要重新对函数参数赋值: +``` - > 容易导致bug,另外重新对参数赋值也会导致优化问题。 - ```javascript - // 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) { - // ... - } - ``` +### 属性 - +- 访问属性时使用点符号 +```javascript +const luke = { + jedi: true, + age: 28, +}; +// bad +const isJedi = luke['jedi']; -## 箭头函数 +// good +const isJedi = luke.jedi; +``` -- 当需要使用箭头函数的时候,使用它,但是不要滥用 +- 根据表达式访问属性时使用`[]` - > 当函数逻辑复杂时,不推荐使用箭头函数,而是单独抽出来放在一个函数里。 +```javascript +const luke = { + jedi: true, + age: 28, +}; - ```javascript - // bad - [1, 2, 3].map(function (x) { - const y = x + 1; - return x * y; - }); - - // good - [1, 2, 3].map((x) => { - const y = x + 1; - return x * y; - }); - ``` +function getProp(prop) { + return luke[prop]; +} -- 避免箭头函数与比较操作符混淆 +const isJedi = getProp('je'+'di'); +``` - ```javascript - // bad - const itemHeight = (item) => item.height <= 256 ? item.largeSize : item.smallSize; - - // bad - const itemHeight = (item) => item.height >= 256 ? item.largeSize : item.smallSize; - - // good - const itemHeight = (item) => (item.height <= 256 ? item.largeSize : item.smallSize); - - // good - const itemHeight = (item) => { - const { height, largeSize, smallSize } = item; - return height <= 256 ? largeSize : smallSize; - }; - ``` - -## 类与构造函数 +### 测试 -- 使用`class` 语法。避免直接操作 `prototype` +- 无论用哪个测试框架,都需要写测试。 +- 尽量去写很多小而美的函数,减少突变的发生 +- 小心 stub 和 mock —— 这会让你的测试变得容易出现问题。 +- 100% 测试覆盖率是我们努力的目标,即便实际上很少达到。 +- 每当你修了一个 bug, 都要尽量写一个回归测试。 如果一个 bug 修复了,没有回归测试,很可能以后会再次出问题。 - ```javascript - // 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` 实现继承: - > 为什么?它是一种内置的方法来继承原型功能而不破坏 `instanceof` - ```javascript - // 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` 来实现链式调用 +- 多行注释用 `/** ... */` ```javascript // bad -Jedi.prototype.jump = function () { - this.jumping = true; - return true; -}; +// 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; +} +``` + +- 单行注释用 `//` + +```javascript +// 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; +} +``` + +- 用 `// FIXME:` 给问题注释,用 `// TODO:` 去注释待办 + +### 分号 + +> 为什么?当 JavaScript 遇到没有分号结尾的一行,它会执行 [自动插入分号](https://tc39.github.io/ecma262/#sec-automatic-semicolon-insertion) 这一规则来决定行末是否加分号。如果 JavaScript 在你的断行里错误的插入了分号,就会出现一些古怪的行为。显式配置代码检查去检查没有带分号的地方可以帮助你防止这种错误。 + +```javascript +// bad - raises exception +const luke = {} +const leia = {} +[luke, leia].forEach((jedi) => jedi.father = 'vader') + +// bad - raises exception +const reaction = "No! That’s impossible!" +(async function meanwhileOnTheFalcon() { + // handle `leia`, `lando`, `chewie`, `r2`, `c3p0` + // ... +}()) + +// bad - returns `undefined` instead of the value on the next line - always happens when `return` is on a line by itself because of ASI! +function foo() { + return + 'search your feelings, you know it to be foo' +} + +// good +const luke = {}; +const leia = {}; +[luke, leia].forEach((jedi) => { + jedi.father = 'vader'; +}); + +// good +const reaction = "No! That’s impossible!"; +(async function meanwhileOnTheFalcon() { + // handle `leia`, `lando`, `chewie`, `r2`, `c3p0` + // ... +}()); + +// good +function foo() { + return 'search your feelings, you know it to be foo'; +} +``` + + +### 命名规范 + +- `export default` 导出模块A,则这个文件名也叫 `A.*`, `import` 时候的参数也叫 `A` : + +```javascript +// file 1 contents +class CheckBox { + // ... +} +export default CheckBox; + +// file 2 contents +export default function fortyTwo() { return 42; } + +// file 3 contents +export default function insideDirectory() {} + +// in some other file +// bad +import CheckBox from './checkBox'; // PascalCase import/export, camelCase filename +import FortyTwo from './FortyTwo'; // PascalCase import/filename, camelCase export +import InsideDirectory from './InsideDirectory'; // PascalCase import/filename, camelCase export + +// bad +import CheckBox from './check_box'; // PascalCase import/export, snake_case filename +import forty_two from './forty_two'; // snake_case import/filename, camelCase export +import inside_directory from './inside_directory'; // snake_case import, camelCase export +import index from './inside_directory/index'; // requiring the index file explicitly +import insideDirectory from './insideDirectory/index'; // requiring the index file explicitly + +// good +import CheckBox from './CheckBox'; // PascalCase export/import/filename +import fortyTwo from './fortyTwo'; // camelCase export/import/filename +import insideDirectory from './insideDirectory'; // camelCase export/import/directory name/implicit "index" +// ^ supports both insideDirectory.js and insideDirectory/index.js +``` + +- 当你`export default`一个函数时,函数名用小驼峰,文件名和函数名一致, export 一个结构体/类/单例/函数库/对象 时用大驼峰。 + +```javascript +function makeStyleGuide() { + // ... +} + +export default makeStyleGuide; + + + +const AirbnbStyleGuide = { + es6: { + } +}; + +export default AirbnbStyleGuide; +``` + + +### 标准库 + +> [标准库](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects)中包含一些由于历史原因遗留的工具类 + +- 用 `Number.isNaN` 代替全局的 `isNaN`: + + ```javascript + // 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` + +```javascript +// bad +isFinite('2e3'); // true + +// good +Number.isFinite('2e3'); // false +Number.isFinite(parseInt('2e3', 10)); // true +``` + + +## 类与函数 + +### 函数 + +- 使用命名函数表达式而不是函数声明 + + > 为什么?这是因为函数声明会发生提升,这意味着在一个文件里函数很容易在其被定义之前就被引用了。这样伤害了代码可读性和可维护性。如果你发现一个函数又大又复杂,且这个函数妨碍了这个文件其他部分的理解性,你应当单独把这个函数提取成一个单独的模块。不管这个名字是不是由一个确定的变量推断出来的,别忘了给表达式清晰的命名(这在现代浏览器和类似 babel 编译器中很常见)。这消除了由匿名函数在错误调用栈产生的所有假设。 ([讨论](https://github.com/airbnb/javascript/issues/794)) + + ```javascript + // bad + function foo() { + // ... + } + + // bad + const foo = function () { + // ... + }; + + // good + // lexical name distinguished from the variable-referenced invocation(s) + // 函数表达式名和声明的函数名是不一样的 + const short = function longUniqueMoreDescriptiveLexicalFoo() { + // ... + }; + ``` + + + +- 把立即执行函数包裹在圆括号里: + + > 立即执行函数:Immediately Invoked Function expression = IIFE。 为什么?因为这样使代码读起来更清晰(译者注:我咋不觉得)。 另外,在模块化世界里,你几乎用不着 IIFE。 + + ```javascript + // immediately-invoked function expression (IIFE) + ( ()=> { + console.log('Welcome to the Internet. Please follow me.'); + }() ); + ``` + +- 不要用 `arguments` 命名参数。他的优先级高于每个函数作用域自带的 `arguments` 对象,这会导致函数自带的 `arguments` 值被覆盖: + + ```javascript + // bad + function foo(name, options, arguments) { + // ... + } + + // good + function foo(name, options, args) { + // ... + } + ``` + +- 用默认参数语法而不是在函数里对参数重新赋值 + + ```javascript + // really bad + function handleThings(opts) { + // 如果 opts 的值为 false, 它会被赋值为 {} + // 虽然你想这么写,但是这个会带来一些微妙的 bug。 + opts = opts || {}; + // ... + } + + // still bad + function handleThings(opts) { + if (opts === void 0) { + opts = {}; + } + // ... + } + + // good + function handleThings(opts = {}) { + // ... + } + ``` + +- 把默认参数赋值放在最后面 + + ```javascript + // bad + function handleThings(opts = {}, name) { + // ... + } + + // good + function handleThings(name, opts = {}) { + // ... + } + ``` + +- 不要修改参数,也不要重新对函数参数赋值: + + > 容易导致bug,另外重新对参数赋值也会导致优化问题。 + + ```javascript + // 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) { + // ... + } + ``` + + + + + +### 箭头函数 + +- 当需要使用箭头函数的时候,使用它,但是不要滥用 + + > 当函数逻辑复杂时,不推荐使用箭头函数,而是单独抽出来放在一个函数里。 + + ```javascript + // bad + [1, 2, 3].map(function (x) { + const y = x + 1; + return x * y; + }); + + // good + [1, 2, 3].map((x) => { + const y = x + 1; + return x * y; + }); + ``` + +- 避免箭头函数与比较操作符混淆 + + ```javascript + // bad + const itemHeight = (item) => item.height <= 256 ? item.largeSize : item.smallSize; + + // bad + const itemHeight = (item) => item.height >= 256 ? item.largeSize : item.smallSize; + + // good + const itemHeight = (item) => (item.height <= 256 ? item.largeSize : item.smallSize); + + // good + const itemHeight = (item) => { + const { height, largeSize, smallSize } = item; + return height <= 256 ? largeSize : smallSize; + }; + ``` + + + +### 类与构造函数 + +- 使用`class` 语法。避免直接操作 `prototype` + + ```javascript + // 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` 实现继承: + + > 为什么?它是一种内置的方法来继承原型功能而不破坏 `instanceof` + + ```javascript + // 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` 来实现链式调用 + +```javascript +// bad +Jedi.prototype.jump = function () { + this.jumping = true; + return true; +}; Jedi.prototype.setHeight = function (height) { this.height = height; @@ -677,7 +996,7 @@ class Rey extends Jedi { -## 模块 +### 模块 - 使用(`import`/`export`)模块 @@ -725,153 +1044,49 @@ import baz from './baz'; -## 迭代器与生成器 - -- 不要用迭代器。使用 JavaScript 高级函数代替 `for-in`、 `for-of` - - > 用数组的这些迭代方法: `map()` / `every()` / `filter()` / `find()` / `findIndex()` / `reduce()` / `some()` / ... , 对象的这些方法 `Object.keys()` / `Object.values()` / `Object.entries()` 得到一个数组,就能去遍历对象。 - - ```javascript - 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); - ``` - -## 属性 - -- 访问属性时使用点符号 - -```javascript -const luke = { - jedi: true, - age: 28, -}; - -// bad -const isJedi = luke['jedi']; - -// good -const isJedi = luke.jedi; -``` - -- 根据表达式访问属性时使用`[]` - -```javascript -const luke = { - jedi: true, - age: 28, -}; - -function getProp(prop) { - return luke[prop]; -} - -const isJedi = getProp('je'+'di'); -``` - -## 变量 - -- 不要使用链式变量赋值 - -> 因为会产生隐式的全局变量 - -```javascript -// 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` -``` - - - -- 不要使用一元自增自减运算符(`++`, `--`) - - > 根据 eslint 文档,一元增量和减量语句受到自动分号插入的影响,并且可能会导致应用程序中的值递增或递减的静默错误。 使用 `num + = 1` 而不是 `num ++` 或 `num ++` 语句也是含义清晰的。 - -```javascript - // 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; - -``` +### 迭代器与生成器 + +- 不要用迭代器。使用 JavaScript 高级函数代替 `for-in`、 `for-of` + + > 用数组的这些迭代方法: `map()` / `every()` / `filter()` / `find()` / `findIndex()` / `reduce()` / `some()` / ... , 对象的这些方法 `Object.keys()` / `Object.values()` / `Object.entries()` 得到一个数组,就能去遍历对象。 + ```javascript + 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); + ``` -## 提升 +### 提升 - `var` 声明会被提前到离他最近的作用域的最前面,但是它的赋值语句并没有提前。`const` 和 `let` 被赋予了新的概念 [暂时性死区 (TDZ)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#temporal_dead_zone_tdz)。 重要的是要知道为什么 [typeof 不再安全](https://web.archive.org/web/20200121061528/http://es-discourse.com/t/why-typeof-is-no-longer-safe/15)。 @@ -930,7 +1145,7 @@ function example() { } ``` -## 比较运算符与相等 +### 比较运算符与相等 - 用 `===` 和 `!==` 严格比较而不是 `==` 和 `!=` @@ -970,123 +1185,23 @@ const maybeNull = value1 > value2 ? 'baz' : null; const foo = maybe1 > maybe2 ? 'bar' : maybeNull; ``` -## 注释 - -- 多行注释用 `/** ... */` - -```javascript -// 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; -} -``` +### 事件 -- 单行注释用 `//` +- 当把数据载荷传递给事件时(例如是 DOM 还是像 Backbone 这样有很多属性的事件)。这使得后续的贡献者(程序员)向这个事件添加更多的数据时不用去找或者更新每个处理器。 ```javascript // 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; -} -``` - -- 用 `// FIXME:` 给问题注释,用 `// TODO:` 去注释待办 - -## 分号 - -> 为什么?当 JavaScript 遇到没有分号结尾的一行,它会执行 [自动插入分号](https://tc39.github.io/ecma262/#sec-automatic-semicolon-insertion) 这一规则来决定行末是否加分号。如果 JavaScript 在你的断行里错误的插入了分号,就会出现一些古怪的行为。显式配置代码检查去检查没有带分号的地方可以帮助你防止这种错误。 - -```javascript -// bad - raises exception -const luke = {} -const leia = {} -[luke, leia].forEach((jedi) => jedi.father = 'vader') - -// bad - raises exception -const reaction = "No! That’s impossible!" -(async function meanwhileOnTheFalcon() { - // handle `leia`, `lando`, `chewie`, `r2`, `c3p0` - // ... -}()) +$(this).trigger('listingUpdated', listing.id); -// bad - returns `undefined` instead of the value on the next line - always happens when `return` is on a line by itself because of ASI! -function foo() { - return - 'search your feelings, you know it to be foo' -} +// ... -// good -const luke = {}; -const leia = {}; -[luke, leia].forEach((jedi) => { - jedi.father = 'vader'; +$(this).on('listingUpdated', (e, listingID) => { + // do something with listingID }); - -// good -const reaction = "No! That’s impossible!"; -(async function meanwhileOnTheFalcon() { - // handle `leia`, `lando`, `chewie`, `r2`, `c3p0` - // ... -}()); - -// good -function foo() { - return 'search your feelings, you know it to be foo'; -} ``` - -## 类型转换与强制转换 +### 类型转换与强制转换 - 字符串 @@ -1140,116 +1255,6 @@ const val = parseInt(inputValue, 10); 2147483649 >> 0 //=> -2147483647 ``` -## 命名规范 - -- `export default` 导出模块A,则这个文件名也叫 `A.*`, `import` 时候的参数也叫 `A` : - -```javascript -// file 1 contents -class CheckBox { - // ... -} -export default CheckBox; - -// file 2 contents -export default function fortyTwo() { return 42; } - -// file 3 contents -export default function insideDirectory() {} - -// in some other file -// bad -import CheckBox from './checkBox'; // PascalCase import/export, camelCase filename -import FortyTwo from './FortyTwo'; // PascalCase import/filename, camelCase export -import InsideDirectory from './InsideDirectory'; // PascalCase import/filename, camelCase export - -// bad -import CheckBox from './check_box'; // PascalCase import/export, snake_case filename -import forty_two from './forty_two'; // snake_case import/filename, camelCase export -import inside_directory from './inside_directory'; // snake_case import, camelCase export -import index from './inside_directory/index'; // requiring the index file explicitly -import insideDirectory from './insideDirectory/index'; // requiring the index file explicitly - -// good -import CheckBox from './CheckBox'; // PascalCase export/import/filename -import fortyTwo from './fortyTwo'; // camelCase export/import/filename -import insideDirectory from './insideDirectory'; // camelCase export/import/directory name/implicit "index" -// ^ supports both insideDirectory.js and insideDirectory/index.js -``` - -- 当你`export default`一个函数时,函数名用小驼峰,文件名和函数名一致, export 一个结构体/类/单例/函数库/对象 时用大驼峰。 - -```javascript -function makeStyleGuide() { - // ... -} - -export default makeStyleGuide; - - - -const AirbnbStyleGuide = { - es6: { - } -}; - -export default AirbnbStyleGuide; -``` - - - -## 事件 - -- 当把数据载荷传递给事件时(例如是 DOM 还是像 Backbone 这样有很多属性的事件)。这使得后续的贡献者(程序员)向这个事件添加更多的数据时不用去找或者更新每个处理器。 - -```javascript -// bad -$(this).trigger('listingUpdated', listing.id); - -// ... - -$(this).on('listingUpdated', (e, listingID) => { - // do something with listingID -}); -``` - -## 标准库 - -> [标准库](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects)中包含一些由于历史原因遗留的工具类 - -- 用 `Number.isNaN` 代替全局的 `isNaN`: - - ```javascript - // 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` - -```javascript -// bad -isFinite('2e3'); // true - -// good -Number.isFinite('2e3'); // false -Number.isFinite(parseInt('2e3', 10)); // true -``` - - - -## 测试 - -- 无论用哪个测试框架,都需要写测试。 -- 尽量去写很多小而美的函数,减少突变的发生 -- 小心 stub 和 mock —— 这会让你的测试变得容易出现问题。 -- 100% 测试覆盖率是我们努力的目标,即便实际上很少达到。 -- 每当你修了一个 bug, 都要尽量写一个回归测试。 如果一个 bug 修复了,没有回归测试,很可能以后会再次出问题。 -