|
JavaScript速查表
- 本手册绝大部分内容是从Airbnb JavaScript Style Guide精简整理,将开发者们都明确的操作去掉,目的为了就是更快的速查。
此处为源地址。
- 译制:HaleNing
目录
基础知识
类型
- 基本类型
最新的 ECMAScript 标准定义了 8 种数据类型,分别是
- string
- number
- bigint
- boolean
- null
- undefined
- symbol (ECMAScript 2016新增)
所有基本类型的值都是不可改变的。但需要注意的是,基本类型本身和一个赋值为基本类型的变量的区别。变量会被赋予一个新值,而原值不能像数组、对象以及函数那样被改变。
- 引用类型
- Object(包含普通对象-Object,数组对象-Array,正则对象-RegExp,日期对象-Date,数学函数-Math,函数对象-Function)
- 使用 typeof 运算符检查:
- undefined:typeof instance === "undefined"
- Boolean:typeof instance === "boolean"
- Number:typeof instance === "number"
- String:typeof instance === "string
- BigInt:typeof instance === "bigint"
- Symbol :typeof instance === "symbol"
- null:typeof instance === "object"。
- Object:typeof instance === "object"
复制代码 引用
推荐常量赋值都使用const, 值可能会发生改变的变量赋值都使用 let。
为什么?let const 都是块级作用域,而 var是函数级作用域
- // bad
- var count = 1;
- if (true) {
- count += 1;
- }
- // good, use the let and const
- let count = 1;
- const pi =3.14;
- if (true) {
- count += 1;
- }
复制代码 对象
- 使用字面语法创建对象:
- // bad
- const item = new Object();
- // good
- const item = {};
复制代码 - 在创建具有动态属性名称的对象时使用属性名称:
- function getKey(k) {
- return `a key named ${k}`;
- }
- // bad
- const obj = {
- id: 5,
- name: 'San Francisco',
- };
- obj[getKey('enabled')] = true;
- // good
- const obj = {
- id: 5,
- name: 'San Francisco',
- [getKey('enabled')]: true,
- };
复制代码 - 属性值简写,并且推荐将缩写 写在前面 :
- const lukeSkywalker = 'Luke Skywalker';
- //常量名就是你想设置的属性名
- // bad
- const obj = {
- lukeSkywalker: lukeSkywalker,
- };
- // good
- const obj = {
- lukeSkywalker,
- };
- const anakinSkywalker = 'Anakin Skywalker';
- const lukeSkywalker = 'Luke Skywalker';
- // good
- const obj = {
- lukeSkywalker,
- anakinSkywalker,
- episodeOne: 1,
- twoJediWalkIntoACantina: 2,
- episodeThree: 3,
- mayTheFourth: 4,
- };
复制代码 - 不要直接调用 Object.prototype上的方法,如 hasOwnProperty、propertyIsEnumerable、isPrototypeOf
为什么?在一些有问题的对象上,这些方法可能会被屏蔽掉,如:{ hasOwnProperty: false } 或空对象 Object.create(null)
- // bad
- console.log(object.hasOwnProperty(key));
- // good
- console.log(Object.prototype.hasOwnProperty.call(object, key));
- // best
- const has = Object.prototype.hasOwnProperty;
- console.log(has.call(object, key));
- /* or */
- import has from 'has'; // https://www.npmjs.com/package/has
- console.log(has(object, key));
复制代码 - 对象拷贝时,推荐使用...运算符来代替Object.assign, 获取大对象的多个属性时,也推荐使用...运算符
- // very bad, 因为line2的操作更改了original
- const original = { a: 1, b: 2 };
- const copy = Object.assign(original, { c: 3 });
- // 将不需要的属性删除了
- delete copy.a;
- // bad
- const original = { a: 1, b: 2 };
- const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }
- // good 使用 es6 扩展运算符 ...
- const original = { a: 1, b: 2 };
- // 浅拷贝
- const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }
- // rest 解构运算符
- const { a, ...noA } = copy; // noA => { b: 2, c: 3 }
复制代码 数组
- 用扩展运算符做数组浅拷贝,类似上面的对象浅拷贝:
- // 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];
复制代码 - 使用 Array.from 而不是 ... 运算符去做 map 遍历。 因为这样可以避免创建一个临时数组:
- // bad
- const baz = [...foo].map(bar);
- // good
- const baz = Array.from(foo, bar);
复制代码 - 如果一个数组有很多行,在数组的 [ 后和 ] 前断行 :
- // good
- const arr = [[0, 1], [2, 3], [4, 5]];
- const objectInArray = [
- {
- id: 1,
- },
- {
- id: 2,
- },
- ];
- const numberInArray = [
- 1,
- 2,
- ];
复制代码 解构
- 用对象的解构赋值来获取和使用对象某个或多个属性值:
- // 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];
- const four = arr[3];
- // good
- const [first, second, _,four] = arr;
复制代码 - 多个返回值用对象的解构,而不是数组解构:
- // bad
- function processInput(input) {
- return [left, right, top, bottom];
- }
- // 数组解构,必须明确前后顺序
- const [left, __, top] = processInput(input);
- // good
- function processInput(input) {
- return { left, right, top, bottom };
- }
- // 只需要关注值,而不用关注顺序
- const { left, top } = processInput(input);
复制代码 字符串
- 当需要动态生成字符串时,使用模板字符串而不是字符串拼接:
- // bad
- function sayHi(name) {
- return 'How are you, ' + name + '?';
- }
- // bad
- function sayHi(name) {
- return ['How are you, ', name, '?'].join();
- }
- // good 可读性比上面更强
- function sayHi(name) {
- return `How are you, ${name}?`;
- }
复制代码 - 永远不要使用 eval(),该方法有太多漏洞。
变量
因为会产生隐式的全局变量
- // 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 ++ 语句也是含义清晰的。
- // 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;
复制代码 属性
- 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('je'+'di');
复制代码 测试
- 无论用哪个测试框架,都需要写测试。
- 尽量去写很多小而美的函数,减少突变的发生
- 小心 stub 和 mock —— 这会让你的测试变得容易出现问题。
- 100% 测试覆盖率是我们努力的目标,即便实际上很少达到。
- 每当你修了一个 bug, 都要尽量写一个回归测试。 如果一个 bug 修复了,没有回归测试,很可能以后会再次出问题。
公共约束
注释
- // 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;
- }
复制代码
- 用 // FIXME: 给问题注释,用 // TODO: 去注释待办
分号
为什么?当 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 :
- // 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 一个结构体/类/单例/函数库/对象 时用大驼峰。
- function makeStyleGuide() {
- // ...
- }
- export default makeStyleGuide;
- const AirbnbStyleGuide = {
- es6: {
- }
- };
- export default AirbnbStyleGuide;
复制代码 标准库
标准库中包含一些由于历史原因遗留的工具类
- 用 Number.isNaN 代替全局的 isNaN:
- // 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
- // bad
- isFinite('2e3'); // true
- // good
- Number.isFinite('2e3'); // false
- Number.isFinite(parseInt('2e3', 10)); // true
复制代码 类与函数
函数
- 使用命名函数表达式而不是函数声明
为什么?这是因为函数声明会发生提升,这意味着在一个文件里函数很容易在其被定义之前就被引用了。这样伤害了代码可读性和可维护性。如果你发现一个函数又大又复杂,且这个函数妨碍了这个文件其他部分的理解性,你应当单独把这个函数提取成一个单独的模块。不管这个名字是不是由一个确定的变量推断出来的,别忘了给表达式清晰的命名(这在现代浏览器和类似 babel 编译器中很常见)。这消除了由匿名函数在错误调用栈产生的所有假设。 (讨论)
- // 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。
- // immediately-invoked function expression (IIFE)
- ( ()=> {
- console.log('Welcome to the Internet. Please follow me.');
- }() );
复制代码 - 不要用 arguments 命名参数。他的优先级高于每个函数作用域自带的 arguments 对象,这会导致函数自带的 arguments 值被覆盖:
- // bad
- function foo(name, options, arguments) {
- // ...
- }
- // good
- function foo(name, options, args) {
- // ...
- }
复制代码 - 用默认参数语法而不是在函数里对参数重新赋值
- // really bad
- function handleThings(opts) {
- // 如果 opts 的值为 false, 它会被赋值为 {}
- // 虽然你想这么写,但是这个会带来一些微妙的 bug。
- opts = opts || {};
- // ...
- }
- // still bad
- function handleThings(opts) {
- if (opts === void 0) {
- opts = {};
- }
- // ...
- }
- // good
- function handleThings(opts = {}) {
- // ...
- }
复制代码 - 把默认参数赋值放在最后面
- // bad
- function handleThings(opts = {}, name) {
- // ...
- }
- // good
- function handleThings(name, opts = {}) {
- // ...
- }
复制代码 - 不要修改参数,也不要重新对函数参数赋值:
容易导致bug,另外重新对参数赋值也会导致优化问题。
- // 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) {
- // ...
- }
复制代码 箭头函数
<ul>当需要使用箭头函数的时候,使用它,但是不要滥用
当函数逻辑复杂时,不推荐使用箭头函数,而是单独抽出来放在一个函数里。
- // 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;
- });
复制代码 避免箭头函数与比较操作符混淆- // 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;
- };
复制代码
- 自定义 toString() 方法是可以的,但需要保证它可以正常工作
- // 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;
- }
- }
复制代码
- 如果没有特别定义,类有默认的构造方法。一个空的构造函数或只是代表父类的构造函数是不需要写的。
- // 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];
- }
- }
复制代码 模块
- // 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()
- .setHeight(20);
复制代码变化通常都是需要避免,特别是当你要输出可变的绑定。虽然在某些场景下可能需要这种技术,但总的来说应该导出常量。
- class Jedi {
- constructor(options = {}) {
- this.name = options.name || 'no name';
- }
- getName() {
- return this.name;
- }
- toString() {
- return `Jedi - ${this.getName()}`;
- }
- }
复制代码
- import JavaScript文件不用包含扩展名
- // 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';
- }
- }
复制代码 迭代器与生成器
- 不要用迭代器。使用 JavaScript 高级函数代替 for-in、 for-of
用数组的这些迭代方法: map() / every() / filter() / find() / findIndex() / reduce() / some() / ... , 对象的这些方法 Object.keys() / Object.values() / Object.entries() 得到一个数组,就能去遍历对象。
- // 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
- let foo = 3;
- export { foo }
- // good
- const foo = 3;
- export { foo }
复制代码
- 已命名函数表达式提升他的变量名,不是函数名或函数体
- // bad
- import foo from './foo.js';
- import bar from './bar.jsx';
- import baz from './baz/index.jsx';
- // good
- import foo from './foo';
- import bar from './bar';
- import baz from './baz';
复制代码 比较运算符与相等
- 用 === 和 !== 严格比较而不是 == 和 !=
- 条件语句,例如if语句使用coercion与tobooleant抽象方法评估它们的表达式,始终遵循这些简单的规则:
- Objects evaluate to true
- Undefined evaluates to false
- Null evaluates to false
- Booleans evaluate to the value of the boolean
- Numbers evaluate to false if +0, -0, or NaN, otherwise true
- Strings evaluate to false if an empty string '', otherwise true
- 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);
复制代码- // 我们知道这个不会工作,假设没有定义全局的 notDefined
- function example() {
- console.log(notDefined); // => throws a ReferenceError
- }
- // 在你引用的地方之后声明一个变量,他会正常输出是因为变量提升。
- // 注意: declaredButNotAssigned 的值 true 没有被提升。
- function example() {
- console.log(declaredButNotAssigned); // => undefined
- var declaredButNotAssigned = true;
- }
- // 可以写成如下例子, 二者意义相同。
- function example() {
- let declaredButNotAssigned;
- console.log(declaredButNotAssigned); // => undefined
- declaredButNotAssigned = true;
- }
- // 用 const,let就不一样了。
- function example() {
- console.log(declaredButNotAssigned); // => throws a ReferenceError
- console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
- const declaredButNotAssigned = true;
- }
复制代码 事件
- 当把数据载荷传递给事件时(例如是 DOM 还是像 Backbone 这样有很多属性的事件)。这使得后续的贡献者(程序员)向这个事件添加更多的数据时不用去找或者更新每个处理器。
- 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');
- };
- }
- // 函数名和变量名一样是也如此。
- function example() {
- console.log(named); // => undefined
- named(); // => TypeError named is not a function
- var named = function named() {
- console.log('named');
- };
- }
复制代码 类型转换与强制转换
- if ([0] && []) {
- // true
- // an array (even an empty one) is an object, objects will evaluate to true
- }
复制代码
- 数字: 用 Number 做类型转换,parseInt 转换 string 应总是带上进制位
- // bad
- const foo = maybe1 > maybe2
- ? "bar"
- : value1 > value2 ? "baz" : null;
- // better
- const maybeNull = value1 > value2 ? 'baz' : null;
- const foo = maybe1 > maybe2
- ? 'bar'
- : maybeNull;
- // best
- const maybeNull = value1 > value2 ? 'baz' : null;
- const foo = maybe1 > maybe2 ? 'bar' : maybeNull;
复制代码
- 移位运算要小心
移位运算对大于 32 位的整数会导致意外行为。Discussion. 最大的 32 位整数是 2,147,483,647:
- // bad
- $(this).trigger('listingUpdated', listing.id);
- // ...
- $(this).on('listingUpdated', (e, listingID) => {
- // do something with listingID
- });
复制代码 推荐资源
- 网站:
- MDN: 不管你是仅仅开始入门、学过一点基础或者是个网站开发老手,你都能在这里找到有用的资源。
- JS周刊 : 你可以在这里,接收到JS社区里最新的动态,其他开发者编写的优秀工具,阅读优秀的文章。
- 印记中文 : JS及其前端领域的文档集合。
- 书籍(为了尊重作者的版权,下列书籍仅开源书籍提供链接):
来源:https://www.cnblogs.com/zhaoqingqing/p/18403172
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作! |
|