Airbnb JavaScript 风格指南

编程入门 行业动态 更新时间:2024-10-07 03:26:37

Airbnb JavaScript <a href=https://www.elefans.com/category/jswz/34/1768003.html style=风格指南"/>

Airbnb JavaScript 风格指南

目录

      • 1. 类型
        • 1.1 基本类型: 你可以直接获取到基本类型的值。(直接存取)
        • 1.2 复杂类型: 复杂类型赋值是获取到他的引用的值。
      • 2. 引用
        • 2.1 所有的赋值都用 const,避免使用 var。eslint: prefer-const, no-const-assign
        • 2.2 如果你一定要对参数重新赋值,使用 let,而不是 var。eslint: no-var
        • 2.3 let 和 const 都是块级作用域
        • 2.4 思考:为什么要用`let`代替`var`
      • 3. 对象
        • 3.1 使用字面值创建对象
        • 3.2 使用计算属性名创建一个带有动态属性名的对象
        • 3.3 用对象方法简写
        • 3.4 用属性值缩写
        • 3.5 将你的所有缩写放在对象声明的前面
        • 3.6 只对那些无效的标示使用引号 `''`
        • 3.7 不要直接调用 Object.prototype上的方法,如 hasOwnProperty、propertyIsEnumerable、isPrototypeOf。
        • 3.8 对象浅拷贝时,更推荐使用扩展运算符(即 ... 运算符),而不是 Object.assign。获取对象指定的几个属性时,用对象的 rest 解构运算符(即 ... 运算符)更好。
      • 4. 数组
        • 4.1 用字面量创建数组
        • 4.2 用 Array#push 代替直接向数组中添加一个值
        • 4.3 用扩展运算符做数组浅拷贝
        • 4.4 用 ... 运算符而不是 Array.from 来将一个可迭代的对象转换成数组
        • 4.5 用 Array.from 将一个类数组对象转成一个数组
        • 4.6 用 Array.from 而不是 ... 运算符去做 map 遍历。 因为这样可以避免创建一个临时数组
        • 4.7 在数组方法的回调函数中使用 return 语句。如果函数体由一条返回一个表达式的语句组成,并且这个表达式没有副作用, 这个时候可以忽略 return
        • 4.8如果一个数组有很多行,在数组的 `[` 后和 `]` 前断行。请看下面示例:

1. 类型

1.1 基本类型: 你可以直接获取到基本类型的值。(直接存取)

stringnumberbooleannullundefinedsymbolbigint

  • symbol为ES6新引入的原始数据类型,表示独一无二的值,引入原因是为了从根本上防止对象属性名的冲突,故其作用为作为对象属性的标识符。
  • BigInt 是通过在整数末尾附加 n 或调用构造函数来创建的。BigInt 可以表示任意大的整数。
const foo = 1;
let bar = foo;bar = 9;console.log(foo, bar); // => 1, 9

由于 Symbols 和 BigInts 不能被正确的 polyfill。所以不应在不能原生支持这些类型的环境或浏览器中使用他们。

理解这句话:

polyfill是用JavaScript来实现一些浏览器不支持的原生API。
例如,querySelectorAll是很多现代浏览器都支持的原生Web API,但是有些古老的浏览器并不支持,那么假设有人写了库,只要用了这个库, 你就可以在古老的浏览器里面使用document.querySelectorAll,使用方法跟现代浏览器原生API无异。那么这个库就可以称为Polyfill或者Polyfiller。一个Polyfill是用来抹平新老浏览器标准原生API 之间的差距。

目前bigInt浏览器支持情况并不理想,只有 Chrome 支持较好,其他浏览器支持不好。由于和其他 JavaScript 新特性不同,BigInt 不能很好的被编译为 ES5。因为 BigInt 中修改了运算符的工作行为,这些行为是不能直接被 polyfill 转换的。
如果要使用bigInt目前,更好的选择是使用JSBI库,纯JS实现了bigInt。使用JSBI的一个优点是,一旦浏览器支持,就不需要重写代码。 相反,可以使用babel插件自动将JSBI代码编译为原生 BigInt代码。

Babel是一个 JavaScript编译器。他把最新版的javascript编译成当下可以执行的版本,即 利用babel就可以让我们在当前的项目中随意的使用这些新最新的es6,甚至es7的语法。babel作用:(1)语法转换(将高级语法解析为当前可用的实现)(2)源代码转换(codemods)(3)Polyfill 目标环境中缺少的功能(通过如 core-js 的第三方 polyfill)babel-core:Babel编译器的核心,Babel默认只转换新的javascript句法,而不转换新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象,因此,转换这些新的API需要一个垫片库polyfill。
1.2 复杂类型: 复杂类型赋值是获取到他的引用的值。

objectarrayfunction

const foo = [1, 2];
const bar = foo;bar[0] = 9;console.log(foo[0], bar[0]); // => 9, 9

2. 引用

2.1 所有的赋值都用 const,避免使用 var。eslint: prefer-const, no-const-assign
2.2 如果你一定要对参数重新赋值,使用 let,而不是 var。eslint: no-var
  • 声明创建一个值时用 const 而不用 var,这样可以保证你声明的值不会被重定义
    在多人开发一个项目时,比如都定义了一个变量a,但各自用途不同,后面定义的a会把前面定义的覆盖掉。
2.3 let 和 const 都是块级作用域
// const 和 let 都只存在于它被定义的那个块级作用域。
{let a = 1;const b = 1;
}
console.log(a); 	// 报错a未定义
console.log(b); 	// 报错
2.4 思考:为什么要用let代替var

JS中存在变量提升(变量提升:JavaScript引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。这造成的结果,就是所有的变量的声明语句,都会被提升到代码的头部,这就叫做变量提升(hoisting)。带varfunction挂关键字会导致变量提升)
变量提升会带来一些问题:

1. 变量可能会被覆盖掉
var myname = 1;
function showName() {//var myname(变量提升操作)console.log(myname);    //undefinedif(0) {var myname = 2;}console.log(myname);    //undefined
}
showName();
2. 本应销毁的变量没有被销毁

function foo(){// var i(变量提升)for (var i = 0; i < 7; i++) {}console.log(i);   //7
}
foo()

在创建执行上下文阶段,变量 i 就已经被提升了,所以当 for 循环结束之后,变量 i 并没有被销毁。

ES6 引入的 letconst 关键字就是为了解决变量提升带来的问题。

function varTest() {var x = 1;if (true) {var x = 2;  // 同样的变量!console.log(x);  // 2}console.log(x);  // 2
}

有两个地方都定义了变量 x,第一个地方在函数块的顶部,第二个地方在 if 块的内部,由于 var 的作用范围是整个函数,所以在编译阶段,最终只生成了一个变量 x,函数体内所有对 x 的赋值操作都会直接改变变量环境中的 x 值。

function letTest() {let x = 1;if (true) {let x = 2;  // 不同的变量console.log(x);  // 2}console.log(x);  // 1
}

let支持块级作用域,代码块内部定义的变量在代码块外部是访问不到的,并且等该代码块中的代码执行完成之后,代码块中定义的变量会被销毁。

3. 对象

3.1 使用字面值创建对象

不用new关键字,用{}

// bad
const item = new Object();// good
const item = {};
3.2 使用计算属性名创建一个带有动态属性名的对象

创建拥有动态属性名的对象时,用计算属性名来表示,这样可以在创建对象时,将所有的属性写在同一个地方。

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,
};
3.3 用对象方法简写
// bad
const atom = {value: 1,addValue: function (value) {return atom.value + value;},
};// good
const atom = {value: 1,// 对象的方法addValue(value) {return atom.value + value;},
};
3.4 用属性值缩写

如果属性名和属性值一样的话,可以缩写。

const lukeSkywalker = 'Luke Skywalker';// bad
const obj = {lukeSkywalker: lukeSkywalker,
};// good
const obj = {lukeSkywalker,
};
3.5 将你的所有缩写放在对象声明的前面

因为这样能更方便地知道有哪些属性用了缩写。

const anakinSkywalker = 'Anakin Skywalker';
const lukeSkywalker = 'Luke Skywalker';// bad
const obj = {episodeOne: 1,twoJediWalkIntoACantina: 2,lukeSkywalker,episodeThree: 3,mayTheFourth: 4,anakinSkywalker,
};// good
const obj = {lukeSkywalker,anakinSkywalker,episodeOne: 1,twoJediWalkIntoACantina: 2,episodeThree: 3,mayTheFourth: 4,
};
3.6 只对那些无效的标示使用引号 ''

通常我们认为这种方式主观上更易读。不仅优化了代码高亮,而且也更容易被许多 JS 引擎优化。

// bad
const bad = {'foo': 3,'bar': 4,'data-blah': 5,
};// good
const good = {foo: 3,bar: 4,'data-blah': 5,
};
3.7 不要直接调用 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'; // 
console.log(has(object, key));

使用Object.prototype.hasOwnProperty.call()的原因:

  • 如果使用 Object.create(null) 创建的对象的原型是null,而不是继承自Object.prototype,所以没有hasOwnProperty方法,调用只会得到 undefined
  • 如果hasOwnPropertyobject的原型链中被重写
  • 如果hasOwnPropertyobject上被重新声明
3.8 对象浅拷贝时,更推荐使用扩展运算符(即 … 运算符),而不是 Object.assign。获取对象指定的几个属性时,用对象的 rest 解构运算符(即 … 运算符)更好。
// very bad,拷贝引用地址,改变copy,同时也会改变original的值
const original = { a: 1, b: 2 };
const copy = Object.assign(original, { c: 3 }); 
delete copy.a; // so does this// bad,只改变第一个对象,所以这里original并没有改变
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }// good es6 扩展运算符 ...,返回一个新的对象,copy是一个新的对象,在内存中另开辟了一个新的空间。
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 }

4. 数组

4.1 用字面量创建数组

不用new,用[]

// bad
const items = new Array();// good
const items = [];
4.2 用 Array#push 代替直接向数组中添加一个值
const someStack = [];// bad
someStack[someStack.length] = 'abracadabra';// good
someStack.push('abracadabra');

原因:vue针对数组重写了push方法,使用push更新数组vue才会监听到,进而更新视图。反之,使用数组下标修改数组不能触发视图的更新。

4.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];
4.4 用 … 运算符而不是 Array.from 来将一个可迭代的对象转换成数组

可迭代对象:若一个对象或其原型链中的任意一个对象带有 Symbol.iterator 键(key)的属性,则这个对象就是可迭代对象。
可迭代对象有:StringArrayMap(Map 对象保存键值对,并且能够记住键的原始插入顺序) 和 Set (Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用)。

Array.from() 方法对一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。

const foo = document.querySelectorAll('.foo');// good
const nodes = Array.from(foo);// best
const nodes = [...foo];
4.5 用 Array.from 将一个类数组对象转成一个数组

类数组:有索引和length属性,但可能没有原生数组的其他方法。

const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };// bad,相当于把参数全部截取,然后返回成一个新数组。本质就是arrLike这个对象使用了数组的slice这个方法
const arr = Array.prototype.slice.call(arrLike);// good
const arr = Array.from(arrLike);
4.6 用 Array.from 而不是 … 运算符去做 map 遍历。 因为这样可以避免创建一个临时数组
// bad
const baz = [...foo].map(bar);// good,Array.from可以接收第二个参数,作用类似于数组的map方法,数组中的每个元素会执行该回调函数。
const baz = Array.from(foo, bar);
4.7 在数组方法的回调函数中使用 return 语句。如果函数体由一条返回一个表达式的语句组成,并且这个表达式没有副作用, 这个时候可以忽略 return
// good
[1, 2, 3].map((x) => {const y = x + 1;return x * y;
});// good 函数只有一个语句
[1, 2, 3].map(x => x + 1);// bad - 没有返回值, 因为在第一次迭代后 acc 就变成 undefined 了。
//reduce,对数组中的每个元素执行一个我们提供的函数,结果汇总为单个返回值。acc就是累计器,上一次调用回调函数时,回调函数返回的值。
[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {const flatten = acc.concat(item);acc[index] = flatten;
});// good,结果[0, 1, 2, 3, 4, 5]
[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {const flatten = acc.concat(item);acc[index] = flatten;return flatten;
});// 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;
});
4.8如果一个数组有很多行,在数组的 [ 后和 ] 前断行。请看下面示例:
// bad
const arr = [[0, 1], [2, 3], [4, 5],
];const objectInArray = [{id: 1,
}, {id: 2,
}];const numberInArray = [1, 2,
];// good
const arr = [[0, 1], [2, 3], [4, 5]];const objectInArray = [{id: 1,},{id: 2,},
];const numberInArray = [1,2,
];

待补充…

参考:
1. polyfill----
2. 变量提升----=%257B%2522request%255Fid%2522%253A%2522164437829116780271988463%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=164437829116780271988463&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~rank_v31_ecpm-4-110489515.first_rank_v2_pc_rank_v29&utm_term=js+var的缺陷&spm=1018.2226.3001.4187
3. 扩展运算符和Object.assign的区别----=%257B%2522request%255Fid%2522%253A%2522164447964616781683919322%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=164447964616781683919322&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~rank_v31_ecpm-7-103233926.first_rank_v2_pc_rank_v29&utm_term=Object.assign和扩展运算符的区别&spm=1018.2226.3001.4187

更多推荐

Airbnb JavaScript 风格指南

本文发布于:2024-03-23 21:27:02,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1743021.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:风格   指南   Airbnb   JavaScript

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!