前端代码指南(三) —— javascript

前端代码指南(一)
前端代码指南(二)
前端代码指南(三)

注: 下面ES6的语法

性能

性能中最重要的是可读性、正确性和可表达性,JavaScript基本上永远不会成为你的性能瓶颈。我们可以通过图像压缩、网络存取和减少回流等来优化性能。如果你看过整篇篇文章后只能记住一条,请记住这条。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 糟糕的 (albeit way faster)
const arr = [1, 2, 3, 4];
const len = arr.length;
var i = -1;
var result = [];
while (++i < len) {
var n = arr[i];
if (n % 2 > 0) continue;
result.push(n * n);
}
// 推荐的
const arr = [1, 2, 3, 4];
const isEven = n => n % 2 == 0;
const square = n => n * n;
const result = arr.filter(isEven).map(square);

无领域

尽量保持你函数的纯净。理想地,应该创建无副作用的、不使用外部数据的并返回新对象(而不是改变现有的)的函数。

1
2
3
4
5
6
7
// 糟糕的
const merge = (target, ...sources) => Object.assign(target, ...sources);
merge({ foo: "foo" }, { bar: "bar" }); // => { foo: "foo", bar: "bar" }
// 推荐的
const merge = (...sources) => Object.assign({}, ...sources);
merge({ foo: "foo" }, { bar: "bar" }); // => { foo: "foo", bar: "bar" }

原生

尽可能依赖原生的方法

1
2
3
4
5
6
7
// 糟糕的
const toArray = obj => [].slice.call(obj);
// 推荐的
const toArray = (() =>
Array.from ? Array.from : obj => [].slice.call(obj)
)();

强制

不要在不必要的时候使用隐形强制

1
2
3
4
5
// 糟糕的
if (x === undefined || x === null) { ... }
// 推荐的
if (x == undefined) { ... }

循环

不要使用循环,因为他们强迫你使用可变的对象。可以依赖array.prototype方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 糟糕的
const sum = arr => {
var sum = 0;
var i = -1;
for (;arr[++i];) {
sum += arr[i];
}
return sum;
};
sum([1, 2, 3]); // => 6
// 推荐的
const sum = arr =>
arr.reduce((x, y) => x + y);
sum([1, 2, 3]); // => 6

如果你不能避免使用循环,或者使用array.prototype方法对你来说是一种受虐的行为,可以使用递归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 糟糕的
const createDivs = howMany => {
while (howMany--) {
document.body.insertAdjacentHTML("beforeend", "<div></div>");
}
};
createDivs(5);
// 糟糕的
const createDivs = howMany =>
[...Array(howMany)].forEach(() =>
document.body.insertAdjacentHTML("beforeend", "<div></div>")
);
createDivs(5);
// 推荐的
const createDivs = howMany => {
if (!howMany) return;
document.body.insertAdjacentHTML("beforeend", "<div></div>");
return createDivs(howMany - 1);
};
createDivs(5);

参数

忘记arguments对象。使用其余的参数才是更好的选择,这是因为:

  1. 其余的参数是被命名的,所以你可以给这个函数的参数一个更好的注意
  2. 其余的参数是一个真正的数组,这使得它更容易使用。
1
2
3
4
5
6
// 糟糕的
const sortNumbers = () =>
Array.prototype.slice.call(arguments).sort();
// 推荐的
const sortNumbers = (...numbers) => numbers.sort();

Apply

忘记Apply()函数,使用扩展来代替

1
2
3
4
5
6
7
8
const greet = (first, last) => `Hi ${first} ${last}`;
const person = ["John", "Doe"];
// 糟糕的
greet.apply(null, person);
// 推荐的
greet(...person);

绑定

忘记bind()函数,这里有个更惯用的方法

1
2
3
4
5
// 糟糕的
["foo", "bar"].forEach(func.bind(this));
// 推荐的
["foo", "bar"].forEach(func, this);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 糟糕的
const person = {
first: "John",
last: "Doe",
greet() {
const full = function() {
return `${this.first} ${this.last}`;
}.bind(this);
return `Hello ${full()}`;
}
}
// 推荐的
const person = {
first: "John",
last: "Doe",
greet() {
const full = () => `${this.first} ${this.last}`;
return `Hello ${full()}`;
}
}

高层级的函数

在不必需的时候避免使用嵌套函数

1
2
3
4
5
// 糟糕的
[1, 2, 3].map(num => String(num));
// 推荐的
[1, 2, 3].map(String);

结构

避免函数的多层次调用,合理使用结构来解决问题

1
2
3
4
5
6
7
8
9
10
const plus1 = a => a + 1;
const mult2 = a => a * 2;
// 糟糕的
mult2(plus1(5)); // => 12
// 推荐的
const pipeline = (...funcs) => val => funcs.reduce((a, b) => b(a), val);
const addThenMult = pipeline(plus1, mult2);
addThenMult(5); // => 12

缓存

缓存功能测试、大数据结构和任何昂贵的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 糟糕的
const contains = (arr, value) =>
Array.prototype.includes
? arr.includes(value)
: arr.some(el => el === value);
contains(["foo", "bar"], "baz"); // => false
// 推荐的
const contains = (() =>
Array.prototype.includes
? (arr, value) => arr.includes(value)
: (arr, value) => arr.some(el => el === value)
)();
contains(["foo", "bar"], "baz"); // => false

变量

const优于 let优于 var

1
2
3
4
5
6
7
// 糟糕的
var me = new Map();
me.set("name", "Ben").set("country", "Belgium");
// 推荐的
const me = new Map();
me.set("name", "Ben").set("country", "Belgium");

条件

if+return语句 优于 if...else if...elseswitch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 糟糕的
var grade;
if (result < 50)
grade = "糟糕的";
else if (result < 90)
grade = "推荐的";
else
grade = "excellent";
// 推荐的
const grade = (() => {
if (result < 50)
return "糟糕的";
if (result < 90)
return "推荐的";
return "excellent";
})();

对象迭代

避免使用for...in

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const shared = { foo: "foo" };
const obj = Object.create(shared, {
bar: {
value: "bar",
enumerable: true
}
});
// 糟糕的
for (var prop in obj) {
if (obj.hasOwnProperty(prop))
console.log(prop);
}
// 推荐的
Object.keys(obj).forEach(prop => console.log(prop));

把对象作为maps

虽然对象是一个合法的用例,但map是一个更好更强大的选择

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 糟糕的
const me = {
name: "Ben",
age: 30
};
var meSize = Object.keys(me).length;
meSize; // => 2
me.country = "Belgium";
meSize++;
meSize; // => 3
// 推荐的
const me = new Map();
me.set("name", "Ben");
me.set("age", 30);
me.size; // => 2
me.set("country", "Belgium");
me.size; // => 3

柯里化

对许多开发者来说,柯里化是一个带有国外范的强大形式。合理化地使用它是非常妙的,但是请不要滥用它

1
2
3
4
5
6
7
// 糟糕的
const sum = a => b => a + b;
sum(5)(3); // => 8
// 推荐的
const sum = (a, b) => a + b;
sum(5, 3); // => 8

可读性

不要使用一些表面看起来很巧妙的技巧来导致你代码的意思模糊不清(即不可读)

1
2
3
4
5
// 糟糕的
foo || doSomething();
// 推荐的
if (!foo) doSomething();

1
2
3
4
5
// 糟糕的
void function() { /* IIFE */ }();
// 推荐的
(function() { /* IIFE */ }());
1
2
3
4
5
// 糟糕的
const n = ~~3.14;
// 推荐的
const n = Math.floor(3.14);

代码复用

不要抗拒去创建一些体积小,高度可组合、可重用的函数。

1
2
3
4
5
6
7
// 糟糕的
arr[arr.length - 1];
// 推荐的
const first = arr => arr[0];
const last = arr => first(arr.slice(-1));
last(arr);

1
2
3
4
5
6
7
// 糟糕的
const product = (a, b) => a * b;
const triple = n => n * 3;
// 推荐的
const product = (a, b) => a * b;
const triple = product.bind(null, 3);

依赖性

减少依赖。第三方的代码是你不了解的,不要仅仅为了几个可复制的函数而加载整个第三方文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 糟糕的
var _ = require("underscore");
_.compact(["foo", 0]));
_.unique(["foo", "foo"]);
_.union(["foo"], ["bar"], ["foo"]);
// 推荐的
const compact = arr => arr.filter(el => el);
const unique = arr => [...Set(arr)];
const union = (...arr) => unique([].concat(...arr));
compact(["foo", 0]);
unique(["foo", "foo"]);
union(["foo"], ["bar"], ["foo"]);