前端入坑依赖前前后后写了好几个项目,在用JavaScript写交互逻辑的时候,或多或少写了一些垃圾代码,如全局变量污染、代码复用性差、简洁性不高等直接给代码后期维护的造成一些困惑。下面是一些JS编码方面有待提高的地方,可直接在开发中加以应用,致力于写出更优雅的代码。
说到代码规范,我们或许会想到ESLint规则,下面的规范有涉及到ESLint规则的进行了相关的说明,也许在你使用ESLint的时候出现相关报错提示也可以从中或许一些帮助。
1.变量声明
1.1不要用var声明变量,尽量使用const
eslint: prefer-const, no-const-assign
避免使用var能够减少全局变量污染问题,使用const确保声明的变量是唯一的,无法在对其进行重新赋值操作。
//bad
var a = 1;
//best
const a = 1;
1.2如果需要声明可变动的引用,那么使用let代替var
eslint: no-var jscs: disallowVar
let属于当前{}中的一个块级作用域,而var属于函数作用域
//bad
var count = 1;
if (true) {
var count = 2;
}
console.log(count)
//best
let count = 1;
if (true) {
let count = 2;
}
console.log(count)
1.3将声明的let和const进行分组
能够提高代码可读性。
//bad
let a = 1;
const obj = {};
let num = 0;
const page = 10;
//best
let a = 1;
let num = 0;
const obj = {};
const page = 10;
1.4将let和const声明的变量放在合适位置
因为let和const被赋予了一种称为【暂时性死区(Temporal Dead Zones, TDZ)】的概念,也就决定了他们声明的变量不会进行变量提升。而var声明的变量会被提升到作用域顶部。
2.使用对象
2.1使用字面量创建对象
eslint: no-new-object
//bad
const obj = new Object();
//good
const obj = {};
2.2对象的方法是用简写形式
// bad
const atom = {
value: 1,
addValue: function (value) {
return atom.value + value;
},
};
// good
const atom = {
value: 1,
addValue(value) {
return atom.value + value;
},
};
2.3对象的属性也使用简写形式
eslint: object-shorthand jscs: requireEnhancedObjectLiterals
const hello = "你好";
//bad
const obj = {
hello:hello
};
//best
const obj = {
hello,
};
2.4不要直接使用Object.prototype的方法,如:hasOwnProperty, propertyIsEnumerable, isPrototypeOf 等
// bad
console.log(object.hasOwnProperty(key));
// good
console.log(Object.prototype.hasOwnProperty.call(object, key));
// best
const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope.
const has = require('has');
…
console.log(has.call(object, key));
2.5对象的浅拷贝最好使用 ... 而不是Object.assign()
// very bad
const original = { a: 1, b: 2 };
const copy = Object.assign(original, { c: 3 }); // this mutates `original`
delete copy.a; // so does this
// bad
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }
// good
const original = { a: 1, b: 2 };
const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }
const { a, ...noA } = copy; // noA => { b: 2, c: 3 }
使用Object.assign()会产生一些意想不到的问题。
3.使用数组
3.1使用字面量创建数组
eslint: no-array-constructor
// bad
const arr= new Array();
// good
const arr= [];
3.2使用扩展运算符 ... 复制数组
// bad
const arr= new Array();
// good
const arr= [];
// bad
const len = arr.length;
const arrCopy = [];
let i;
for (i = 0; i < len; i++) {
arrCopy[i] = arr[i];
}
// good
const arrCopy = [...arr];
3.3使用Array.from把一个类数组转成数组
const list = document.getElementsByTagName("li");
const liNodes = Array.from(list);
4.函数
4.1使用函数声明代替函数表达式
为什么?因为函数声明的函数会先被识别,进行变量提升(hoisted),而函数表达式只会把函数的引用变量名提升(即变量提升)。
// bad
const fn= function () {
};
// good
function fn() {
}
4.2不要再一个非函数代码块中(if,else,while等)声明函数, 而是把那个函数赋给一个变量。即使前者不会报错,但是浏览器的解析方式是不同的。
// bad
if (ifLogin) {
function test() {
console.log(' logged');
}
}
// good
let test;
if (ifLogin) {
test = () => {
console.log(' logged');
};
}
4.3避免使用arguments,而是用rest语法 ... 替代
原因是arguments是一个类数组,没有数组特有的方法,而...能够明确你传入的参数,并且是真正的数组。
// bad
function myconcat() {
const args = Array.prototype.slice.call(arguments);
return args.join('');
}
// good
function myconcat(...args) {
return args.join('');
}
5.箭头函数
5.1当你必须使用函数表达式(或者需要传递一个匿名函数)时候,可以使用箭头函数代替。
原因是使用新的函数会创建一个新的函数作用域,这样就会改变当前this的指向,而箭头函数会创建一个新的this执行环境,能够将当前环境的this继续传递下去;并且写法也更为简洁。
当你的函数较为复杂的时候,这时候使用箭头函数就容易出问题,可以使用函数声明代替。
// bad
[1, 3, 5].map(function (x) {
return x * x;
});
// good
[1, 3, 5].map((x) => {
return x * x;
});
5.2如果一个函数适合用一行写出并且只有一个参数,那就把花括号、圆括号和 return 都省略掉。如果不是,那就不要省略。
// good
[1, 2, 3].map(x => x * x);
// good
[1, 2, 3].reduce((total, n) => {
return total + n;
}, 0);
6.构造器
6.1总是使用class,避免直接操作prototype属性
这样写更为简洁。
// 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;
}
}
7.模块开发
7.1利用模块的思想写业务。
使用模块编写逻辑业务,可以使你的代码更有整体性和可扩展性。类似的库有seaJS、requireJS
7.2少使用通配符import
这样更能够确保你只有一个模块被你import,而那些不必要的模块不会被import,减少代码体积。
// bad
import * as webUI from './WEB';
// good
import webUI from './WEB';
8.使用高阶函数如map()和reduce()代替for~of
const arr= [1, 2, 3, 4, 5];
// bad
let sum = 0;
for (let num of arr) {
sum += num;
}
sum === 15;
// good
let sum = 0;
arr.forEach((num) => sum += num);
sum === 15;
// best (use the functional force)
const sum = arr.reduce((total, num) => total + num, 0);
sum === 15;
9.比较运算符
9.1优先使用===和!==而不是==和!=
===和!==不会进行强制类型转换,后者则会
9.2在做if条件判断时强制类型转换规则
- 对象都会被转为true
- null、undefined、NaN被转为false
- 布尔值转为对应的布尔值
- 数字中+0 -0 0都被计算为false,否则为true
- 字符串 如果是“”空字符串,被计算为fasle,否则为true
- ...