以下是我最喜欢的一些编写更干净的 Javascript 代码的技巧,跳过聪明的保持简单。
使用 const 或 let 声明所有变量。根据经验,默认情况下使用 const,否则如果需要重新赋值变量,请使用 let。
应避免使用 var 关键字,因为它几乎是无范围的,会导致潜在的范围错误。
例如:最佳做法是在创建变量时初始化变量,以便您和您的团队可以确保没有未使用的变量。
// ❌ Avoid this
var old = "";
// ✅ Do this
const immutable = "John";
let counter = 1;
counter++; // counter === 2;
// Declare objects and arrays as const to prevent type change
const user = {firstname: "John", lastname: "Doe"};
const users = ["Mac", "Roe"];
严格相等运算符 (===) 与相等运算符相同,检查其两个操作数是否相等,并返回布尔结果。但与相等运算符 (==) 不同,严格相等运算符 (===) 始终认为不同类型的操作数是不同的。
例如:0为 false,在非严格相等运算符的情况下会错误地等于 false。
// ❌ Avoid this
1 == "1"; // true
0 == false; // true
// ✅ Do this
1 === 1; // true
1 === "1"; // false
0 === false; // false
原始对象与原始对象构造函数严格不同,这使得
它们在包装在对象中时更难检查严格相等性。
它们基本上是等价的,但其实不相等。
// ❌ Avoid this
const stringObject = new String("Charly");
// ✅ Do this
const stringPrimitive = "Charly";
// Equality check
stringPrimitive === stringObject; // false
new Number(1) === 1; // false
new Boolean(true) === true; // false
对象结构是一种速记符号,允许您动态定义对象或数组。
从而避免重复,提高可读性并防止错误,因为我们无法推断其背后的逻辑,我们当前是在初始化变量还是更新变量?
// ❌ Avoid this
const user = new Object(); // {}
user.firstname = "John"; // { firstname: "John" }
user.lastname = "Doe"; // { firstname: "John", lastname: "Doe" }
// ✅ Do this
const user = {
firstname: "John",
lastname: "Doe"
};
// ❌ Avoid this
const fruits = new Array(); // []
fruits.push("banana"); // ["banana"]
fruits.push("mango"); // ["banana", "mango"]
// ✅ Do this
const fruits = ["banana", "mango"];
将字符串合并是一件很痛苦的事情,尤其是在组合字符串和变量时。
为了简化此过程,您可以使用模板文本(用反引号标记),只要字符串和变量值 (${}) 包围,它就会同时接受字符串和变量。
const firstname = "John";
// ❌ Avoid this
let greeting = "Hello, " + firstname; // Hello, John
// ✅ Do this
greeting = `Hello, ${firstname}`; // Hello, John
使用分号进行行终止始终是一种很好的做法。
如果您忘记了它,您不会收到警告,因为在大多数情况下,它会被 JavaScript 解析器插入。但是,如果没有它,你怎么知道一个表情何时结束呢?
以for循环为例:
// ✅ Do this
for (let i = 0; i < numbers.length; i++) {
console.log(numbers[i]);
}
您将无法执行以下操作,因为解析器认为它是一个表达式,而实际上它是三个独立的表达式:
// ❌ Not this
for (let i = 0 i < numbers.length i++) {
console.log(numbers[i]);
} // Uncaught SyntaxError: Unexpected identifier
我认为在我的函数中定义太多参数是一种代码坏味道。即使参数具有默认值或可选参数,我们也可以举个例子:
// ❌ Avoid this
function avatarUrl(avatar, format = "small", caption = true) {
// Does something
}
avatarUrl(user.avatar, 'thumb', false)
当您使用此函数时,很难知道使用了哪些参数以及如何使用。最后一个参数 false 在这里代表什么?
不知道,我们必须打开函数定义才能知道。
如果需要更改参数的顺序,会发生什么情况?好吧,您必须更改所有函数调用。
对于对象,顺序无关紧要:
// ✅ Do this
function avatarUrl(avatar, options={format: 'small', caption: true}) {
// Does something
}
avatarUrl(user.avatar, {
format: "thumb",
caption: false
})
嵌套条件使代码难以理解,但您可以通过提前返回来使用 guard 子句轻松避免它。
Guard 子句将允许您删除大部分 else 条件,使您的代码像纯文本一样可读。
// ❌ Avoid this
function doSomething() {
if (user) {
if (user.role === "ADMIN") {
return 'Administrator';
} else {
return 'User';
}
} else {
return 'Anonymous';
}
}
// ✅ Do this
function doSomething() {
if (!user) return 'Anonymous'
if (user.role === "ADMIN") return 'Administrator'
return 'User'
}
Javascript 在数组、对象、字符串上提供了许多内置函数。
查找并学习它们,以享受提供的全部功能。
// ❌ Avoid this
const users = [
{
username: "JohnDoe",
admin: false
},
{
username: "Todd",
admin: true
},
];
const admins = [];
function getAdmins() {
users.forEach((user) => {
if (user.admin) admins.push(user)
})
return admins
}
// ✅ Do this
function getAdmins() {
return users.filter((user) => user.admin)
}
让我们假设一下,我们大多数人都不善于注意到差异,我们可能需要几秒钟才能注意到逻辑非(!
让我们举个例子:
const users = [
{
username: "JohnDoe",
admin: false
enabled: true
},
{
username: "Todd",
admin: true
enabled: true
},
];
// ❌ Avoid this
const members = users.filter(u => u.enabled).map(u => !u.admin)
const admins = users.filter(u => u.enabled).map(u => u.admin)
// ✅ Do this
const enabledUsers = users.filter(u => u.enabled)
const members = enabledUsers.map(u => !u.admin)
const admins = enabledUsers.map(u => u.admin)
members 和 admins 分配的区别仅在逻辑非 (!),如果您需要更改一个分配,则还需要更改另一个分配。
另一个例子,不要使用魔法数字。请改用显式变量:
// ❌ Avoid this
function price_with_taxes(price) {
return price * 1.2
}
// ✅ Do this
const taxRate = 1.2
function price_with_taxes(price) {
return price * taxRate
}
无论你是为事件写 e,还是为票证写 t 都不会提高你的工作效率,但它确实会降低可读性并降低即时理解力。
// ❌ Avoid this
function someFunction() {
events.forEach(e => {
e.tickets.forEach(t => {
`${e.name} for ${t.full_name}`
})
})
}
// ✅ Do this
function someFunction() {
events.forEach(event => {
event.tickets.forEach(ticket => {
`${event.name} for ${ticket.full_name}`
})
})
}
在这里,您不必猜测 e 和 t 代表什么,您只需阅读它即可。
编码非常复杂,足以让你的大脑变得异常复杂。这也适用于变量、类、方法......
不过也有少数例外,可以在 for 循环中使用像 i 这样广泛使用的缩写。
条件就像你大脑的备忘录,因为你需要在逐步执行每一行代码时记住它们,以便了解发生了什么。
幸运的是,由于我最喜欢的 ES6 运算符 optional,它们中的大多数都可以简化。
// ❌ Avoid this
function doSomething(params) {
if (params && params.filter) return 'Foo'
return 'Bar'
}
// ✅ Do this
function doSomething(params) {
if (params?.filter) return 'Foo'
return 'Bar'
}
我不知道你是怎么想的,但每次我看到一个逻辑非(!),它都会让我的大脑停顿一秒钟,对我来说,阅读这样的东西感觉更自然:
如果用户是管理员,那么我们会这样做
而不是:
如果用户不是管理员,那么我们会这样做。
// ❌ Avoid this
function doSomething(user) {
if (!user || !user.admin) {
// Case where no user or not admin
} else {
// Case where user and user is admin
}
}
// ✅ Do this
function doSomething(user) {
if (user && user.admin) {
// Case where user and user is admin
} else {
// Case where no user or not admin
}
}
使用 for...of 语句而不是经典的 for 循环就是这样一种 JavaScript 改进。
ES6 引入了这种语法,它包含一个内置的迭代器,因此您不必定义自己的变量,而是将其递增,直到达到某个长度值:
let users = ["Fedor Emelianenko", "Cyril Gane", "Conor McGregor"];
// ❌ Avoid this
// This avoids length behind reavaluated at every iteration
let usersCount = users.length;
for (let i = 0; i < usersCount; i++) {
console.log(users[i]);
}
// ✅ Do this
for(let user of users) {
console.log(user);
}
请注意它的可读性有多强!而且你不必关心所有这些笨拙的逻辑(let i = 0;i < usersCount;i++),尽管你可能需要它来处理某些特定的用例,比如不规则的间隔。
永远记住,你是在与其他开发人员以及你未来的自己一起编写代码。您不希望创建比编写代码时要解决的问题更多的问题。
不要为了炫耀自己的技能而编写代码,要编写每个人都能理解和调试的代码。
Keep it simple!