JavaScript 基础语法——词法结构:从字符到代码的基石

JavaScript(简称 JS)作为 Web 开发的基石语言,其语法规则决定了代码如何被解析和执行。而词法结构(Lexical Structure) 是语法的基础,它定义了 JavaScript 代码的基本“积木”——包括字符编码、大小写规则、空格处理、注释、标识符、关键字、字面量、运算符等。理解词法结构是写出正确、可维护代码的第一步,也是排查语法错误的关键。本文将系统梳理 JavaScript 词法结构的核心内容,结合实例和最佳实践,帮助读者夯实基础。

目录#

  1. 字符与编码
  2. 大小写敏感性
  3. 空白符与行终止符
  4. 注释
  5. 标识符与保留字
  6. 字面量(Literals)
  7. 运算符
  8. 自动分号插入(ASI)
  9. 总结与最佳实践
  10. 参考资料

1. 字符与编码#

JavaScript 代码由字符组成,其字符集基于 Unicode 标准(可表示世界上几乎所有语言的字符)。具体来说:

  • 编码格式:JS 引擎通常使用 UTF-8 编码解析代码(兼容 ASCII),支持多字节字符(如中文、日文、emoji 等)。
  • 字符范围:除了常规的字母、数字、符号,还支持 Unicode 转义序列(如 \u00A9 表示版权符号 ©)。

示例:Unicode 字符在代码中的使用#

// 变量名包含中文(合法但不推荐,见最佳实践)
let 用户名 = "张三";
console.log(用户名); // 输出:张三
 
// 使用 emoji 作为变量名(合法但不推荐)
const 😊 = "笑脸";
console.log(😊); // 输出:笑脸
 
// Unicode 转义序列
const copyright = "\u00A9 2024";
console.log(copyright); // 输出:© 2024

最佳实践:#

  • 优先使用 ASCII 字符:尽管 JS 支持 Unicode 标识符,但非 ASCII 字符可能降低代码可读性(尤其团队协作时),且部分编辑器或工具可能不兼容。
  • 避免特殊符号:除非必要(如 $ 用于框架变量,如 jQuery 的 $),否则变量名尽量使用字母、数字、_$

2. 大小写敏感性#

JavaScript 是大小写敏感(Case-Sensitive) 的语言,即同一字符的大小写被视为不同的符号。

示例:大小写差异导致的变量区分#

let name = "Alice";
let Name = "Bob";
let NAME = "Charlie";
 
console.log(name);  // 输出:Alice(小写)
console.log(Name);  // 输出:Bob(首字母大写)
console.log(NAME);  // 输出:Charlie(全大写)

常见陷阱:#

  • 关键字大小写错误:如 If(错误) vs if(正确),Function(错误) vs function(正确)。
  • API 方法大小写:如 console.log 不可写成 console.Log

最佳实践:#

  • 严格区分大小写:养成变量、函数名大小写一致的习惯,避免因拼写错误导致的 BUG。
  • 遵循命名规范:如变量用 camelCase(小驼峰)、构造函数用 PascalCase(大驼峰)、常量用 UPPER_SNAKE_CASE(全大写+下划线)。

3. 空白符与行终止符#

空白符(Whitespace)和行终止符(Line Terminators)用于分隔代码元素,增强可读性,但 JS 引擎会忽略多余的空白。

空白符类型:#

  • 空格( )、制表符(\t)、垂直制表符(\v)、换页符(\f)。
  • 行终止符:换行符(\n)、回车符(\r)、回车+换行(\r\n)、行分隔符(\u2028)、段落分隔符(\u2029)。

示例:空白符的作用#

// 以下代码等效,空白符不影响执行
let a=1;let b=2;console.log(a+b);
 
let a = 1; 
let b = 2; 
console.log(a + b); // 输出:3(可读性更佳)

注意事项:#

  • 行终止符可能触发 ASI(见第 8 节):部分场景下,换行可能被 JS 引擎解释为分号。
  • 空白符不能出现在标识符中间:如 user name(错误),需用 userNameuser_name

最佳实践:#

  • 统一缩进:使用 2 或 4 个空格(避免混合空格和制表符),提升代码可读性。
  • 合理换行:长代码行(如超过 80-120 字符)应换行,保持代码整洁。

4. 注释#

注释用于解释代码逻辑,不参与执行。JS 支持两种注释方式:

4.1 单行注释(//#

// 开头,注释范围从 // 到行尾。

// 这是单行注释
let age = 18; // 年龄变量(行内注释)

4.2 多行注释(/* */#

/* 开头,*/ 结尾,可跨多行。

/*
  这是多行注释
  用于描述复杂逻辑
*/
function add(a, b) {
  return a + b;
}

最佳实践:#

  • 注释“为什么”而非“是什么”:代码本身应清晰表达“做什么”,注释需解释“为什么这么做”(如业务逻辑、特殊处理的原因)。
  • 避免过时注释:代码更新时同步更新注释,否则注释会误导读者。
  • 禁用注释掉的代码:废弃代码应直接删除,而非注释保留(版本控制工具如 Git 可追溯历史)。

5. 标识符与保留字#

标识符(Identifier) 是用于命名变量、函数、类、属性等的名称。保留字(Reserved Words) 是 JS 预留的关键字,不能用作标识符。

5.1 标识符命名规则:#

  • 必须以 字母a-zA-Z)、下划线_)或 美元符号$)开头。
  • 后续字符可包含字母、数字(0-9)、_$
  • 不能包含空格、标点符号(除 _$)。
  • 区分大小写(如 ageAge 是不同标识符)。

示例:合法与非法标识符#

// 合法标识符
let userName;
let _privateVar;
let $total;
let count123;
 
// 非法标识符(会报错)
let 123var;      // 以数字开头
let user-name;   // 包含连字符
let if;          // 是保留字
let my name;     // 包含空格

5.2 保留字#

JS 保留字分为 关键字(如 iffunction)、未来保留字(如 classenum)和 严格模式保留字(如 implementspackage)。部分保留字在非严格模式下可作为标识符,但强烈不推荐。

常见关键字breakcasecatchclassconstcontinuedebuggerdefaultdeletedoelseexportextendsfinallyforfunctionifimportininstanceofnewreturnsuperswitchthisthrowtrytypeofvarvoidwhilewithyield

最佳实践:#

  • 见名知意:标识符名称应清晰表达其含义(如 userAge 而非 ua)。
  • 遵循命名规范
    • 变量/函数:camelCase(如 calculateTotal)。
    • 类/构造函数:PascalCase(如 UserAccount)。
    • 常量:UPPER_SNAKE_CASE(如 MAX_RETRY_COUNT)。
    • 私有成员(约定):前缀 _(如 _internalMethod)。

6. 字面量(Literals)#

字面量是直接在代码中表示的值,是 JS 数据类型的直接体现。常见字面量类型如下:

6.1 数字字面量#

表示数值,支持整数、浮点数、十六进制、八进制、二进制和 BigInt。

// 十进制整数
let intNum = 42;
 
// 浮点数
let floatNum = 3.14;
 
// 科学计数法
let sciNum = 1e3; // 1000(1 × 10³)
let sciNum2 = 2e-2; // 0.02(2 × 10⁻²)
 
// 十六进制(前缀 0x)
let hexNum = 0xFF; // 255
 
// 二进制(前缀 0b)
let binNum = 0b1010; // 10
 
// 八进制(前缀 0o)
let octNum = 0o17; // 15(十进制)
 
// BigInt(后缀 n,用于超大型整数)
let bigNum = 9007199254740993n;

6.2 字符串字面量#

表示文本,支持单引号(')、双引号(")和模板字面量( )。

// 单引号/双引号(功能相同,选一种保持一致)
let str1 = 'Hello';
let str2 = "World";
 
// 模板字面量(支持多行和变量插值,用 ` ` 包裹)
let name = "Alice";
let multiLine = `Hello ${name},
This is a multi-line string.`;
console.log(multiLine);
// 输出:
// Hello Alice,
// This is a multi-line string.

6.3 布尔字面量#

仅两个值:true(真)和 false(假)。

let isDone = false;
let hasPermission = true;

6.4 null 和 undefined#

  • null:表示“空值”(主动赋值)。
  • undefined:表示“未定义”(变量声明未赋值时的默认值)。
let emptyValue = null;
let unassignedVar; // undefined

6.5 对象字面量#

{} 表示,包含键值对(属性)。

let user = {
  name: "Bob",
  age: 30,
  isStudent: false
};

6.6 数组字面量#

[] 表示,包含有序元素列表。

let fruits = ["apple", "banana", "cherry"];

6.7 符号字面量(Symbol)#

Symbol() 创建,是唯一且不可变的值,用于对象属性的唯一键。

let uniqueKey = Symbol("id");
let obj = { [uniqueKey]: "value" };

最佳实践:#

  • 字符串统一引号:项目中统一使用单引号或双引号(如 ESLint 规则 quotes)。
  • 模板字面量优先:多行字符串或需要变量插值时,优先使用模板字面量(`)。
  • 避免八进制陷阱:非严格模式下,以 0 开头的数字可能被解析为八进制(如 010 是 8),建议用 0o 前缀显式表示八进制。

7. 运算符#

运算符用于对值进行操作,JS 支持多种运算符,按功能可分为:

常见运算符分类:#

  • 算术运算符+(加)、-(减)、*(乘)、/(除)、%(取余)、**(幂)、++(自增)、--(自减)。
  • 赋值运算符=+=-=*= 等。
  • 比较运算符==(相等)、===(严格相等)、!=(不等)、!==(严格不等)、><>=<=
  • 逻辑运算符&&(与)、||(或)、!(非)。
  • 位运算符&(与)、|(或)、^(异或)、~(非)、<<(左移)、>>(右移)、>>>(无符号右移)。
  • 其他typeof(类型判断)、instanceof(实例判断)、in(属性判断)等。

示例:运算符使用#

// 算术运算符
let sum = 10 + 5; // 15
let power = 2 **3; // 8(2的3次方)
 
// 赋值运算符
let x = 10;
x += 5; // 等价于 x = x + 5 → x=15
 
// 严格相等(推荐,避免类型转换)
console.log(5 === "5"); // false(类型不同)
console.log(5 == "5");  // true(自动类型转换)
 
// 逻辑运算符(短路特性)
let result = true && "Hello"; // "Hello"(&& 取第一个假值,无假值则取最后一个值)
let result2 = false || "World"; // "World"(|| 取第一个真值,无真值则取最后一个值)

最佳实践:#

  • 优先使用严格相等(===)== 会触发隐式类型转换,可能导致意外结果(如 "" == 0true)。
  • 避免连续赋值:如 a = b = c = 0,可读性差,建议拆分。
  • 明确括号优先级:复杂表达式用括号明确运算顺序,避免依赖运算符优先级(如 a + b * c 不如 a + (b * c) 清晰)。

8. 自动分号插入(ASI)#

JavaScript 允许省略语句末尾的分号(;),引擎会自动插入分号(Automatic Semicolon Insertion,ASI)。但 ASI 有规则限制,滥用可能导致逻辑错误。

ASI 触发场景:#

1.** 行终止符后 :若一行代码结束,且下一行开始的代码无法与当前行合并解析,则插入分号。 2. 代码块结束(})前**:如函数、循环、条件语句的 } 前。 3.** 文件结束时**:文件末尾自动插入分号。

ASI 陷阱示例:#

// 陷阱 1:return 后换行
function getValue() {
  return  // ASI 会在这里插入分号,导致返回 undefined
    { name: "Alice" };
}
console.log(getValue()); // undefined(而非 { name: "Alice" })
 
// 陷阱 2:数组/对象字面量换行
let arr = [1, 2, 3]
[4, 5].forEach(x => console.log(x)); // 报错:Cannot read property 'forEach' of undefined
// 原因:引擎将 [4,5] 解析为 arr[4,5](属性访问),而非新数组
 
// 陷阱 3:自增/自减运算符
let a = 1
++a
console.log(a); // 2(正常,但不推荐省略分号)

最佳实践:#

-** 显式添加分号 **:避免依赖 ASI,尤其在以下场景必须加分号:

  • 一行多个语句(如 let a=1; let b=2)。
  • returnbreakcontinue 后换行。
  • [(+-/ 开头的行(如 [1,2].forEach(...) 前若有未加分号的语句)。

9. 总结与最佳实践#

JavaScript 词法结构是代码的“语法基石”,掌握以下核心原则可显著提升代码质量:

1.** 字符与编码 :优先使用 ASCII 字符,避免非标准符号。 2. 大小写敏感 :严格区分大小写,遵循命名规范(camelCasePascalCase 等)。 3. 空白符 :统一缩进(2/4 空格),合理换行提升可读性。 4. 注释 :解释“为什么”,避免过时或冗余注释。 5. 标识符 :见名知意,不使用保留字,遵循命名约定。 6. 字面量**:统一字符串引号,优先用模板字面量处理多行文本。 7.** 运算符**:优先使用 ===,复杂表达式加括号明确优先级。 8.** ASI**:显式添加分号,避免依赖自动插入。

10. 参考资料#

通过深入理解词法结构,我们能写出更规范、更易维护的 JavaScript 代码,为后续学习复杂语法和框架打下坚实基础。