Skip to content

JavaScript 最佳实践

编写清晰、高效、易于维护的 JavaScript 代码需要遵循一些最佳实践。这些实践包括管理作用域(scope)、使用现代变量声明方式、正确处理类型以及采用安全的编码模式。

最小化全局变量:使用模块和局部作用域

Section titled “最小化全局变量:使用模块和局部作用域”

过度使用全局变量(global variables)可能导致命名冲突,并使代码更难管理和调试。在一个脚本中声明的全局变量可能会被另一个脚本意外覆盖。

现代 JavaScript 推崇使用 ES 模块(ES Modules,import/export 语法)来封装代码和管理依赖。这自然地限制了全局作用域(global scope)。

在函数或代码块(block)内,始终使用 let 或 const 声明变量,以使它们具有局部作用域(locally scoped)。

在现代 JavaScript 中,避免使用 var 关键字声明变量。相反,对于可能重新赋值的变量使用 let,对于声明后不应重新赋值的变量(常量)使用 const。

let 和 const 是块级作用域(block-scoped)的,这意味着它们只能在声明它们的代码块(由 { } 定义)内部访问。这有助于防止变量意外泄露并提高代码清晰度。

// Example of block scope
if (true) {
const blockScopedVar = "I'm block-scoped";
let anotherBlockVar = 10;
// console.log(blockScopedVar, anotherBlockVar); // Accessible here
}
// console.log(blockScopedVar); // Error: blockScopedVar is not defined here

在脚本或模块的开头使用 "use strict"; 有助于捕获常见的编码错误,例如使用未声明的变量。请注意,ES 模块默认处于严格模式(strict mode)。

在变量使用前声明它们是一个好的习惯,通常是在它们作用域(函数或块)的开头进行。

在声明变量时进行初始化可以防止 undefined 值引起的意外行为,并使预期的数据类型更清晰。

这提高了代码的可读性,并有助于避免以下错误:

  • 更清晰的代码结构。
  • 在同一作用域内只需查找一个地方即可找到变量声明。
  • 避免了在使用变量前进行赋值相关的错误。
// Declare and initialize at the beginning of their scope
function processOrder(items) {
const taxRate = 0.08;
let subTotal = 0;
let customerName = "Guest";
// ... rest of the function logic
}
for (let i = 0; i < 5; i++) {
// loop logic using i
}
虽然 JavaScript 会提升(hoists)声明,但 let 和 const 有一个“暂时性死区”(Temporal Dead Zone,TDZ),这意味着在代码中声明之前,你无法访问它们。这通常是一件好事,因为它阻止了使用未初始化的变量。

直接使用原始值;避免对象包装器

Section titled “直接使用原始值;避免对象包装器”

始终将数字(numbers)、字符串(strings)和布尔值(booleans)视为原始值(primitive values)。避免使用 new Number()、new String() 或 new Boolean() 将它们创建为对象(objects)。

使用对象包装器会降低执行速度,并可能导致令人困惑的行为,尤其是在比较时:

const primitiveString = "John";
const objectString = new String("John");
console.log(typeof primitiveString); // "string"
console.log(typeof objectString); // "object"
console.log(primitiveString === "John"); // true
console.log(objectString === "John"); // false (object vs string)
console.log(primitiveString === objectString); // false
console.log(new String("John") === new String("John")); // false (different objects)

对对象、数组和函数使用字面量

Section titled “对对象、数组和函数使用字面量”

优先使用字面量(literal)语法来创建对象、数组、正则表达式和函数,而不是使用它们对应的构造函数:

  • 使用 {} 而不是 new Object()
  • 使用 ""(或 ''、`)而不是 new String()
  • 使用 0 而不是 new Number()
  • 使用 false(或 true)而不是 new Boolean()
  • 使用 [] 而不是 new Array()
  • 使用 /()/ 而不是 new RegExp()
  • 使用 function() {} 或 () => {} 而不是 new Function()
const myObject = {}; // Preferred object literal
const myString = ""; // Preferred string literal
const myNumber = 0; // Preferred number literal
const myBoolean = false; // Preferred boolean literal
const myArray = []; // Preferred array literal
const myRegExp = /abc/g; // Preferred RegExp literal
const myFunction = () => {}; // Preferred function expression (arrow function)

警惕自动类型转换;使用严格相等比较

Section titled “警惕自动类型转换;使用严格相等比较”

JavaScript 是弱类型(loosely typed)的,这意味着变量可以改变类型,并且在操作过程中会发生自动类型转换(coercion)。这有时可能导致意外的结果。

let value = "Hello"; // typeof value is "string"
value = 5; // typeof value is now "number"

比较值时,宽松相等运算符(==)会执行类型转换,而严格相等运算符(===)则不会。通常来说,使用严格相等(===)和严格不等(!==)更安全、更可预测。

console.log(0 == ""); // true (string "" coerces to 0)
console.log(0 === ""); // false (number vs string)
console.log(1 == "1"); // true (string "1" coerces to 1)
console.log(1 === "1"); // false (number vs string)
console.log(null == undefined); // true (special case for loose equality)
console.log(null === undefined);// false

如果调用函数时提供的参数少于声明的参数,则缺失的参数将具有 undefined 值。现代 JavaScript(ES6+)允许你在函数签名中直接为函数参数定义默认值(default values)。

这比手动检查 undefined 并赋值默认值更简洁、更具表达力。

// Modern ES6+ way with default parameters
function greet(name = "Guest", greeting = "Hello") {
console.log(`${greeting}, ${name}!`);
}
greet("Alice"); // Output: Hello, Alice!
greet(); // Output: Hello, Guest!

在相关的 JavaScript 函数章节中了解更多关于函数参数和参数的信息。

在 switch 语句结尾包含 default 情况

Section titled “在 switch 语句结尾包含 default 情况”

在 switch 语句中始终包含一个 default 情况是一个好的实践,即使你认为所有可能的情况都已涵盖。这可以优雅地处理意外的值,并使你的代码更健壮。

let dayName;
switch (new Date().getDay()) {
case 0:
dayName = "Sunday";
break;
case 1:
dayName = "Monday";
break;
// ... other cases
case 6:
dayName = "Saturday";
break;
default:
dayName = "Unknown Day"; // Handles unexpected values
// console.error("Unexpected day value");
}

eval() 函数执行一个字符串作为 JavaScript 代码。强烈不建议使用它,因为它存在重大的安全风险(如果字符串来自不可信来源,它可能执行恶意代码)和性能影响。

几乎总是有比 eval() 更安全、更有效的替代方法。解析 JSON 时,使用 JSON.parse()。对于动态属性访问,使用方括号表示法(object[propertyName])。