2JavaScript高级程序设计(第4版)

词法结构

语法基础

标识符字面量注释分号

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第2章:词法结构(Lexical Structure)

一、导读

1.1 本章学习目标

第2章介绍JavaScript的词法结构——这是语言的最底层语法。理解词法结构是正确编写JavaScript代码的前提。本章的核心学习目标包括:

  • 了解JavaScript的字符集:Unicode和UTF-16编码
  • 掌握标识符的命名规则:什么名字是合法的?
  • 认识关键字和保留字:哪些名字不能使用?
  • 理解各种字面量:数字、字符串、布尔值、数组、对象
  • 掌握注释的写法:单行注释和多行注释
  • 理解自动分号插入(ASI)机制:JavaScript何时自动加分号?

1.2 为什么本章如此重要

词法结构是语言的"语法规则的语法"。虽然大多数开发者不会每天思考这些问题,但理解它们能帮助你避免一些诡异的bug。例如,自动分号插入(ASI)机制经常导致意外行为,理解它的工作原理可以避免很多陷阱。

1.3 核心问题引导

JavaScript的标识符可以包含哪些字符?中文可以做变量名吗?

为什么有些关键字被"保留"了但从未使用?

自动分号插入(ASI)在哪些情况下会导致bug?

模板字面量和普通字符串有什么本质区别?

为什么 0.1 + 0.2 !== 0.3?JavaScript如何处理浮点数?


二、核心概念详解

2.1 字符集

JavaScript使用Unicode字符集,源文件通常以UTF-8编码保存。这意味着:

javascript
// 中文变量名是合法的
let 名字 = "Alice";
let 年龄 = 20;
console.log(`${名字}的年龄是${年龄}`);

// 希腊字母也可以
let π = 3.14159;
let α = 0.1, β = 0.2;
console.log(α + β);

2.2 标识符

标识符用于命名变量、函数、类等。规则如下:

  • 第一个字符必须是字母、下划线(_)或美元符号($)
  • 后续字符可以是字母、数字、下划线或美元符号
  • 不能使用关键字和保留字
  • 区分大小写
javascript
// 合法的标识符
let myVariable = "valid";
let _private = "valid";
let $jquery = "valid";
let camelCase = "valid";
let PascalCase = "valid";
let snake_case = "valid";

// 命名约定
let userName = "Alice";      // 变量:camelCase
let MAX_SIZE = 100;          // 常量:UPPER_SNAKE_CASE
function calculateTotal() {} // 函数:camelCase
class UserProfile {}         // 类:PascalCase

2.3 关键字和保留字

javascript
// 关键字(不能使用)
// break, case, catch, class, const, continue, debugger, default,
// delete, do, else, export, extends, finally, for, function, if,
// import, in, instanceof, new, return, super, switch, this, throw,
// try, typeof, var, void, while, with, yield

// 未来保留字
// enum, implements, interface, let, package, private, protected,
// public, static, await

// 特殊值(也不能用作标识符)
// null, true, false

2.4 字面量

字面量是直接写在代码中的值:

javascript
// 数字字面量
let num = 42;
let float = 3.14;
let hex = 0xFF;
let binary = 0b1010;
let octal = 0o12;
let bigint = 9007199254740991n;

// 字符串字面量
let str1 = "hello";
let str2 = 'world';
let str3 = `template`;  // 模板字面量

// 布尔字面量
let bool = true;

// 空值字面量
let empty = null;
let undef = undefined;

// 数组字面量
let arr = [1, 2, 3];

// 对象字面量
let obj = {name: "Alice", age: 20};

// 正则表达式字面量
let regex = /pattern/gi;

2.5 模板字面量

模板字面量(ES6)使用反引号,支持多行字符串和表达式插值:

javascript
let name = "Alice";
let age = 20;

// 表达式插值
let greeting = `Hello, ${name}! You are ${age} years old.`;
console.log(greeting);

// 多行字符串
let html = `
  <div>
    <h1>${name}</h1>
    <p>Age: ${age}</p>
  </div>
`;

// 标签模板
function highlight(strings, ...values) {
  return strings.reduce((result, str, i) => {
    let val = values[i] ? `<mark>${values[i]}</mark>` : '';
    return result + str + val;
  }, '');
}

let result = highlight`Hello ${name}, you are ${age} years old.`;
console.log(result);

2.6 自动分号插入(ASI)

JavaScript解析器会在某些位置自动插入分号。这可能导致意外行为:

javascript
// 这些情况会自动插入分号
let x = 1  // 自动插入
let y = 2  // 自动插入

// 危险情况
let a = 1
let b = a
(function() {
  // 这里会被解析为 a(...) 调用!
  // 而不是 a; (function() {...})
})();

// 解决方案:总是使用分号
let c = 1;
let d = c;
(function() {
  console.log("safe");
})();

// 另一个危险情况
// return语句后面的值如果换行,会自动插入分号
function getValue() {
  return  // 这里自动插入分号!
  {
    value: 42
  };
}
console.log(getValue());  // undefined

// 正确写法
function getValueFixed() {
  return {
    value: 42
  };
}

三、实战案例

3.1 标识符和命名规范

javascript
// 好的命名实践
const MAX_RETRY_COUNT = 3;
const userName = "Alice";
const isUserLoggedIn = true;
const getUserProfile = (id) => { /* ... */ };
class UserProfileComponent { /* ... */ }

// 避免的命名
const a = 42;           // 无意义
const data = [];        // 太模糊
const d = new Date();   // 单字母(循环变量除外)
const temp = 42;        // 临时变量应该有更具体的名字

3.2 模板字面量的高级用法

javascript
// 条件表达式
let isLoggedIn = true;
let message = `Welcome${isLoggedIn ? '' : ' back'}!`;

// 函数调用
let items = [1, 2, 3];
let list = `Items: ${items.join(', ')}`;

// 嵌套模板
let users = [
  {name: "Alice", age: 20},
  {name: "Bob", age: 25}
];

let html = `
  <ul>
    ${users.map(u => `
      <li>${u.name} (${u.age})</li>
    `).join('')}
  </ul>
`;
console.log(html);

四、本章小结

第2章介绍了JavaScript的词法结构:

字符集:JavaScript使用Unicode,支持中文等非ASCII字符作为标识符。

标识符:遵循camelCase(变量/函数)和PascalCase(类)的命名约定。

关键字:有约40个关键字和若干保留字不能用作标识符。

字面量:数字、字符串、布尔值、数组、对象、正则等都有字面量语法。

模板字面量:支持表达式插值和多行字符串,是现代JavaScript的重要特性。

ASI:自动分号插入机制在某些情况下会导致意外行为,推荐总是使用分号。


五、思考与练习

思考题

JavaScript的标识符规则与Java/C有什么异同?

为什么ES6引入了 let 和 const 来替代 var?

自动分号插入(ASI)的设计初衷是什么?它带来了什么问题?

模板字面量相比字符串拼接有什么优势?

为什么 typeof NaN 返回 "number"?

编程练习

编写代码验证各种标识符命名是否合法。

使用模板字面量生成一段HTML代码。

找出3个自动分号插入导致bug的例子并解释原因。


2/14词法结构
0%