使用const 或者 let 替代 var 使用const 声明你不打算改变的值,比如配置,比如对象的引用。
使用let 声明打算改变的值
缩小变量的作用域 不用在开头列出所有的变量,现在推荐使用let 或者 const 就近声明,提高代码的可维护性
用块状作用域代替匿名函数 1 2 3 4 5 6 7 8 9 for (var n = 0 ; n < 10 ; n++){ (function (value ){ setTimeout ( function ( ){ console .log (value); },10 ); })(n) }
使用块状作用率代替匿名函数
1 2 3 4 5 for (let n = 0 ; n < 10 ; n++){ setTimeout (function ( ){ console .log (n); }) }
使用箭头函数代替各种访问this值的变通方式 为了回调中访问上下文的this,使用了这些
旧习惯
使用变量,var self = this
使用 bind
在支持的函数中,使用 thisArg 形参
新习惯
使用箭头函数
在不涉及this,或者大多数情况下,箭头函数都可以代替普通函数,更简洁
使用参数默认值,而不是代码实现 旧习惯
1 2 3 4 5 6 function do (参数 ){ if (参数 === undefined ){ 参数 = 默认值 } }
新的习惯
1 2 3 function do (参数 = 默认值 ){ }
使用 rest 参数替代 arguments 关键字 旧习惯
1 2 3 4 5 6 7 8 9 10 11 12 function func1 (a, b, c ) { console .log (arguments [0 ]); console .log (arguments [1 ]); console .log (arguments [2 ]); }func1 (1 , 2 , 3 );
新习惯
1 2 3 4 5 6 7 8 9 10 11 12 function func1 (...rest ) { console .log (rest[0 ]); console .log (rest[1 ]); console .log (rest[2 ]); }func1 (1 , 2 , 3 );
考虑尾后逗号 旧习惯
1 2 3 4 const temp = { a :1 , b :2 }
新习惯
1 2 3 4 const temp = { a :1 , b :2 , }
好处在哪尼,问题在修改的时候可以少敲一个逗号
使用类创建构造函数 旧习惯
1 2 3 4 5 6 7 8 9 10 11 12 var Bird = function Bird (name ){ this .name = name; }Bird .prototype .talk = function talk (string ){ return this .name + 'talk:' + string; }var bird = new Bird ("小红" ); bird.talk ("Hi!" );
新习惯
1 2 3 4 5 6 7 8 9 10 11 12 class Bird { constructor (name ){ this .name = name; } talk (string ){ return this .name + 'talk:' + string; } }var bird = new Bird ("小红" ); bird.talk ("Hi!" );
创建对象时使用可计算属性名 旧习惯
之前,如果属性名是个变量或者需要动态计算,则只能通过 对象.[变量名] 的方式去访问,且只能运行时才能确定
1 2 3 4 var name = 'answer' ; var obj = { }; obj[name]=42 ;console .log (obj[name])
新习惯
1 2 3 4 5 var name = 'answer' ; var obj = { [name]:42 };console .log (obj[name])
同名变量初始化对象时,使用简写语法 旧习惯:哪怕变量是局部变量,也要写完整key与value
1 2 3 4 5 function getParams ( ){ let name; return {name :name} }
新习惯:使用简写
1 2 3 4 5 function getParams ( ){ let name; return {name} }
使用 *Object.assign()*代替自定义的扩展方法或者显式复制所有属性 旧习惯 使用遍历key然后复制给新的对象
1 2 3 4 5 6 7 8 9 10 11 function mergeObjects ( ) { var resObj = {}; for (var i=0 ; i < arguments .length ; i += 1 ) { var obj = arguments [i], keys = Object .keys (obj); for (var j=0 ; j < keys.length ; j += 1 ) { resObj[keys[j]] = obj[keys[j]]; } } return resObj; }
新习惯 使用Object.assign代替
1 2 3 4 5 const target = { a : 1 , b : 2 };const returnedTarget = Object .assign ({}, target);console .log (returnedTarget);
使用属性扩展语法 旧习惯 基于已有对象创建新对象时,使用 Object.assign
1 2 3 4 5 6 const target = { a : 1 , b : 2 };const source = { b : 3 , c : 4 };const returnedTarget = Object .assign ({},target, source);console .log (returnedTarget);
新习惯 使用属性扩展语法
1 2 3 4 const a = { a : 1 , b : 2 };const b = { b : 3 , c : 4 };const returnedTarget = {...a,...b};
使用 Symbol 避免属性名冲突 旧习惯 使用可读性很差的又很长的字符串来用作属性名来避免冲突
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const shapeType = { triangle : 'Triangle' };function getArea (shape, options ) { let area = 0 ; switch (shape) { case shapeType.triangle : area = .5 * options.width * options.height ; break ; } return area; }getArea (shapeType.triangle , { width : 100 , height : 100 });
新习惯
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const shapeType = { triangle : Symbol () };function getArea (shape, options ) { let area = 0 ; switch (shape) { case shapeType.triangle : area = .5 * options.width * options.height ; break ; } return area; }getArea (shapeType.triangle , { width : 100 , height : 100 });
使用 Object.getPrototypeOf/setPrototypeOf 来代替 Proto 旧习惯 使用非标准的 Proto 来获取或者设置原型
新习惯 使用标准方法 Object.getPrototypeOf/setPrototypeOf 来获取或者设置原型
具体查看 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto
使用对象方法的简写语法来定义对象中的方法 旧习惯 key 与 value 的形式
1 2 3 4 5 const obj = { fun : function ( ){ } }
新习惯
1 2 3 4 5 const obj = { fun ( ){ } }
遍历的姿势-可迭代对象 旧习惯 使用for循环或者 forEach 方法 来循环
1 2 3 4 5 6 for (let i = 0 ; i < array.length ; ++i){ console .log (array[i]); } array.forEach (entry => console .log (entry));
新习惯 当不需要下标的时候,考虑for-of
1 2 3 4 5 6 7 8 9 10 11 12 let arr = [1 , 2 , 3 , 4 ]for (let item of arr) { console .log (item) } 需要下标可以考虑let arr = [1 , 2 , 3 , 4 ]for (let [index, value] of arr.entries ()) { arr[index] = value * 10 }console .log (arr)
遍历的姿势-DOM 旧习惯 为了遍历DOM集合,将DOM集合转为数组或者使用Array.prototype.forEach.call 方法
1 2 3 4 5 6 7 Array .prototype .slice .call (document .querySelectorAll ("div" )).forEach ( (div )=> ) Array .prototype .forEach .call (document .querySelectorAll ("div" ),div => { })
新习惯 确保DOM集合是可迭代的,然后使用for-of
1 2 3 for (const div of document .querySelectorAll ("div" ){ }
在使用 Function.prototype.apply() 的大部分场景就可以使用可迭代对象的展开语法 旧习惯
1 2 3 4 5 6 7 8 9 10 11 const numbers = [5 , 6 , 2 , 3 , 7 ];const max = Math .max .apply (null , numbers);console .log (max);const min = Math .min .apply (null , numbers);console .log (min);
新习惯 使用可迭代对象的展开语法
1 2 3 4 5 6 7 8 9 10 11 const numbers = [5 , 6 , 2 , 3 , 7 ];const max = Math .max ([...numbers]);console .log (max);const min = Math .min ([...numbers]);console .log (min);
该用解构用解构 旧习惯 使用对象中的少量属性就把整个对象保存
1 2 3 const bird = getBird ();console .log (bird.age );console .log (bird.name );
新习惯 使用解构赋值,不需要整个对象的时候
1 2 3 let {age,name} = getBird ();console .log (age);console .log (name);
解构重命名 旧习惯 使用解构赋值,key 不一致
1 2 3 4 5 6 let {age,name} = getBird ();let birdAge = age;let birdName = name;console .log (birdAge); console .log (birdName);
// 新习惯:使用解构赋值并重命名变量
1 2 3 const { age : birdAge, name : birdName } = bird;console .log (birdAge); console .log (birdName);
对可选项使用解构赋值 旧习惯 使用代码处理默认值
1 2 3 4 5 6 7 8 9 10 11 function check (options ){ let options = Object .assign ({},options,{ a :1 , b :2 , }) if (options.c === undefined ){ options.c = options.a + options.b } console .log (options.c ) }
新习惯 将默认值提出来,通过解构来直接处理
1 2 3 4 5 6 7 8 9 10 11 function check ({ a=1 , b=2 , c=a+b } = {} ){ console .log (c) }check ({a :2 })
用 async / await 代替 Promise 旧习惯 使用 Promise
1 2 3 4 5 function fetchJSON (url ){ return fetch (url).then ( (response )=> return response.json (); ) }
新习惯 使用 async/await
1 2 3 4 async function fetchJSON (url ){ const response = await fetch (url); return response.json (); }
使用模板字面量代替字符串连接 旧习惯
1 const getInfo = bird => bird.name + ' ' + bird.age ;
新习惯
1 const getInfo = bird => `${bird.name} ${bird.age} ` ;
使用字符串迭代 旧习惯 使用下标访问字符串中的字符
1 2 3 4 const str = "abcde" ;for (let i = 0 ; i < str.length ; ++i){ }
新习惯 视为可迭代对象
1 2 3 4 const str = "abcde" ;for (const char of str){ }
使用 fing 和 findIndex 方式来代替循环找数组中某个符合条件的 旧习惯 使用for循环 forEach filter取第一项等
1 2 3 4 5 6 7 8 let target;let array = [{id :1 },{id :2 }];for (let i = 0 ; i < array.length ; ++i){ if (array[i].id === 2 ){ target = array[i]; break ; } }
新习惯 使用 fing 和 findIndex
1 2 let array = [{id :1 },{id :2 }];let target = array.find (i => return i.id === 2 );
使用 Array.fill 代替循环填充数组 旧习惯 使用for循环填充数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const len = 5 ;const array = new Array (len);for (let i = 0 ; i < len; ++i){ array[i] = 0 ; }const M = 5 ;const N = 5 ;const array = new Array (M);for (let i = 0 ; i < M; ++i){ array[i] = new Array (N); for (let j = 0 ; i < N; ++j){ array[i][j] = 0 ; } }
新习惯 使用fill
1 2 3 4 5 6 7 8 9 const len = 5 ;const array = new Array (len).fill (0 );const M = 5 ;const N = 5 ;const array = new Array (M).fill (0 ).map (()=> new Array (N).fill (0 ));
读取文件时使用 readAsArrayBuffer 取代 readAsBinaryString 旧习惯 使用readAsBinaryString 来处理文件
新习惯 使用readAsArrayBuffer 来处理文件
使用Map代替对象 旧习惯 使用对象存储
1 2 3 4 const map = {};const id = "1" ;const obj = {}; map[id] = obj;
新习惯 使用Map
1 2 3 4 5 6 7 const store = new Map ();const id = {};const obj = {}; store.set (id,obj);let target = stroe.get (id);
使用Set 代替对象做集合 旧习惯 使用对象的key来标记是否存在
1 2 3 4 5 let birds = {}; birds["xiaohong" ] = true ;if (birds["xiaohong" ]){ }
新习惯 使用Set
1 2 3 4 5 const birds = new Set (); birds.add ("xiaohong" );if (birds.has ("xiaohong" )){ }
使用模块代替伪命名空间 旧习惯 导出一整个对象,jquery时代的操作
1 2 3 4 5 6 var utils = (function (utils ){ function toString ( ){ } })(utils || {})
新习惯 使用ESM的导入导出操作
1 2 3 4 5 6 7 8 9 function toString ( ){ }const utils = { toString :toString }export default utils;export { toString };
参考资料:https://es6.ruanyifeng.com/#docs/module-loader
将CJS、AMD和其他模块的转为 ESM 旧习惯 打包成CJS AMD 等其他格式,因为没有ESM
新习惯 使用ESM格式,至少新的要支持ESM,拥抱未来
使用通用的打包器而不是自研 旧习惯 自研一套打包器
新习惯 拥抱社区,使用通用的有社区支持的打包器
使用代理,而不是提供API来get,set 属性 旧习惯 直接提供整个对象,约定不修改
新习惯 改用代理
使用代理将部分判断与实现分开 旧习惯 将判断与功能实现写在一起
新习惯 可以考虑将功能函数写对象上,判断或者检测在代理上实现
使用二进制字面量 旧习惯 在一些可能更适合二进制的地方,使用十六进制数字
1 2 3 4 5 6 const flags = { a : 0x01 , b : 0x02 , c : 0x04 , d : 0x08 , }
新习惯 在合理的地方采用二进制数字
1 2 3 4 5 6 const flags = { a : 0b00000001 , b : 0b00000010 , c : 0b00000100 , d : 0b00001000 , }
这个多提一句,其实应用挺多的,比如Vue3 里面的 PatchFlags 来标记动静节点就是典型的,比如react的车道标记也是
使用新的Math函数,而不是各种自己实现的变通方式 旧习惯 使用代码自己实现
新习惯 使用新提供的Math 函数
1 2 3 4 ** = Math .pow Math .sign (x); Math .trunc (x);
使用空值合并 旧习惯 代码判断并赋值
1 const data = res.data === null || res.data === undefined ? 200 : res.data ;
新习惯 使用空值合并 ??
1 const data = res.data ?? 200 ;
使用可选链 旧习惯 先判断是否存在再调用
1 2 3 4 5 6 let xiaohong = bird && bird.one && bird.one .name ;if (xiaohong.talk ){ xiaohong.talk (); }
新习惯 使用可选链
注意如果是 Number 或者 布尔数值 仍然会报错
1 2 3 4 let xiaohong = bird?.one ?.name ; xiaohong.talk ?.();