核心知识点
1. JavaScript 简介
1.1 什么是 JavaScript?
JavaScript (JS) 是一种轻量级的、解释型的、具有函数优先(first-class functions)的编程语言。它主要作为 Web 页面的脚本语言而闻名,但也被用于许多非浏览器环境,如 Node.js、Apache CouchDB 和 Adobe Acrobat。JavaScript 是一种基于原型、多范式的动态脚本语言,支持面向对象、命令式和声明式(如函数式编程)风格。
1.2 JavaScript 的特点
- 解释型语言: 代码在运行时逐行解释执行,通常不需要预先编译。
 - 动态类型: 变量的类型在运行时确定,并且可以改变。
 - 基于原型: 对象继承是通过原型链实现的,而不是类(虽然 ES6 引入了 
class语法糖)。 - 函数是一等公民: 函数可以像其他值一样被传递、赋值给变量、作为参数或返回值。
 - 事件驱动和非阻塞 I/O: 特别是在浏览器和 Node.js 环境中,常用于处理用户交互和网络请求等异步操作。
 
1.3 JavaScript 的用途
- 网页交互: DOM 操作、事件处理、动画效果。
 - Web 应用开发: 构建复杂的前端应用(配合 React, Vue, Angular 等框架)。
 - 后端开发: 使用 Node.js 构建服务器、API。
 - 移动应用开发: React Native, NativeScript 等。
 - 桌面应用开发: Electron, NW.js 等。
 - 游戏开发: Phaser, Babylon.js 等。
 
2. 基础语法与数据类型
2.1 变量声明 (var, let, const)
var: 函数作用域或全局作用域,存在变量提升,可以重复声明。let: 块级作用域,不存在变量提升(暂时性死区),不能重复声明。const: 块级作用域,声明时必须初始化,且不能重新赋值(但对象或数组的内容可变)。
// var (avoid in modern JS)
function varTest() {
  console.log(a); // undefined (hoisting)
  if (true) {
    var a = 10;
  }
  console.log(a); // 10 (no block scope)
  var a = 20; // Redeclaration allowed
  console.log(a); // 20
}
varTest();
// console.log(a); // ReferenceError: a is not defined (function scoped)
// let
function letTest() {
  // console.log(b); // ReferenceError: Cannot access 'b' before initialization (TDZ)
  let b = 10;
  if (true) {
    let b = 20; // Different variable in block scope
    console.log("Inner b:", b); // Inner b: 20
  }
  console.log("Outer b:", b); // Outer b: 10
  // let b = 30; // SyntaxError: Identifier 'b' has already been declared
}
letTest();
// const
const PI = 3.14159;
// PI = 3.14; // TypeError: Assignment to constant variable.
const person = { name: "Alice" };
person.name = "Bob"; // OK: Modifying object property
// person = { name: "Charlie" }; // TypeError: Assignment to constant variable. (Cannot reassign the reference)
console.log(person); // { name: 'Bob' }
const arr = [1, 2, 3];
arr.push(4); // OK: Modifying array content
// arr = [5, 6]; // TypeError: Assignment to constant variable.
console.log(arr); // [ 1, 2, 3, 4 ]
2.2 数据类型 (Data Types)
JavaScript 有两类数据类型:原始类型 (Primitive Types) 和对象类型 (Object Type)。
原始类型 (Primitives):
string: 文本数据。number: 数字,包括整数和浮点数 (以及NaN,Infinity)。boolean:true或false。null: 表示有意设置的空值或“无对象”。undefined: 表示变量已声明但未赋值,或对象属性不存在。symbol(ES6): 唯一的、不可变的值,常用作对象属性的键。bigint(ES2020): 表示任意精度的整数。
let str = "Hello";
let num = 123.45;
let isTrue = true;
let nothing = null; // Type is 'object' (historical quirk)
let notAssigned; // Value and Type are 'undefined'
let sym = Symbol("id");
let bigNum = 123456789012345678901234567890n; // Note the 'n' suffix
console.log(typeof str); // "string"
console.log(typeof num); // "number"
console.log(typeof isTrue); // "boolean"
console.log(typeof nothing); // "object" (!!)
console.log(typeof notAssigned); // "undefined"
console.log(typeof sym); // "symbol"
console.log(typeof bigNum); // "bigint"
对象类型 (Object):
Object: 键值对的集合。Array: 有序的元素列表。Function: 可执行的代码块。Date,RegExp,Error等内置对象。
let obj = { key: "value", count: 1 };
let arrData = [1, "two", true, null];
function greet() {
  console.log("Hi!");
}
let today = new Date();
let pattern = /ab+c/;
console.log(typeof obj); // "object"
console.log(typeof arrData); // "object" (Arrays are objects)
console.log(Array.isArray(arrData)); // true (Use Array.isArray to check specifically for arrays)
console.log(typeof greet); // "function" (Functions are special objects)
console.log(typeof today); // "object"
console.log(typeof pattern); // "object"
2.3 运算符 (Operators)
- 算术运算符: 
+,-,*,/,%(取模),**(幂/指数 ES7)。 - 赋值运算符: 
=,+=,-=,*=,/=,%=,**=. - 比较运算符: 
==(宽松相等, 会进行类型转换),!=(宽松不等),===(严格相等, 不进行类型转换),!==(严格不等),>,<,>=,<=. - 逻辑运算符: 
&&(逻辑与),||(逻辑或),!(逻辑非)。 - 位运算符: 
&,|,^,~,<<,>>,>>>. - 三元运算符: 
condition ? exprIfTrue : exprIfFalse. - 类型运算符: 
typeof,instanceof. - 其他: 
delete(删除对象属性),new(创建实例),in(检查属性是否存在于对象或其原型链),,(逗号运算符)。 
// Arithmetic
console.log(5 + 3); // 8
console.log(10 % 3); // 1
console.log(2 ** 4); // 16
// Assignment
let x = 5;
x += 3; // x is now 8
// Comparison (!!! Use strict equality === most of the time !!!)
console.log("5" == 5); // true (loose equality, string "5" converted to number 5)
console.log("5" === 5); // false (strict equality, types are different)
console.log(null == undefined); // true (loose equality special case)
console.log(null === undefined); // false (strict equality)
// Logical
let isValid = true;
let hasPermission = false;
console.log(isValid && hasPermission); // false
console.log(isValid || hasPermission); // true
console.log(!isValid); // false
// Ternary
let age = 20;
let status = age >= 18 ? "adult" : "minor";
console.log(status); // "adult"
// typeof / instanceof
let message = "hello";
let numbers = [1, 2, 3];
console.log(typeof message); // "string"
console.log(numbers instanceof Array); // true
console.log(numbers instanceof Object); // true (Arrays are objects)
2.4 类型转换 (Type Conversion)
JavaScript 是动态类型语言,经常在运算中进行隐式类型转换。也可以显式转换。
- 转字符串: 
String(),value.toString(),+ ""(隐式)。 - 转数字: 
Number(),parseInt(),parseFloat(),+value(隐式)。 - 转布尔: 
Boolean(),!!value(隐式)。Falsy 值 (转为false):false,0,""(空字符串),null,undefined,NaN。其他所有值都是 Truthy。 
// To String
let numVal = 123;
let strVal = String(numVal); // "123"
let boolVal = true;
let strBool = boolVal.toString(); // "true"
let concatStr = numVal + ""; // "123" (Implicit)
console.log(typeof strVal, strVal); // string 123
// To Number
let strNum = "45.6";
let numFromStr = Number(strNum); // 45.6
let intStr = "78px";
let intNum = parseInt(intStr); // 78 (stops at non-digit)
let floatStr = "3.14em";
let floatNum = parseFloat(floatStr); // 3.14
let unaryPlus = +"99"; // 99 (Implicit using unary plus)
let invalidNum = Number("hello"); // NaN (Not a Number)
console.log(typeof numFromStr, numFromStr); // number 45.6
console.log(intNum); // 78
console.log(floatNum); // 3.14
console.log(unaryPlus); // 99
console.log(invalidNum, typeof invalidNum); // NaN number
// To Boolean
console.log(Boolean(0)); // false
console.log(Boolean("")); // false
console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false
console.log(Boolean(NaN)); // false
console.log(Boolean(1)); // true
console.log(Boolean("hello")); // true
console.log(Boolean({})); // true (empty object is truthy)
console.log(Boolean([])); // true (empty array is truthy)
console.log(!!0); // false (Implicit using double negation)
console.log(!!"hi"); // true
3. 控制流 (Control Flow)
3.1 条件语句 (if, else if, else, switch)
if...else if...else: 根据条件执行不同的代码块。switch: 基于一个表达式的值匹配多个case。
// if...else
let score = 75;
if (score >= 90) {
  console.log("Grade A");
} else if (score >= 80) {
  console.log("Grade B");
} else if (score >= 70) {
  console.log("Grade C");
} else {
  console.log("Grade D or F");
}
// Output: Grade C
// switch
let day = new Date().getDay(); // 0 (Sun) to 6 (Sat)
let dayName;
switch (day) {
  case 0:
    dayName = "Sunday";
    break; // Important! Prevents fall-through
  case 1:
    dayName = "Monday";
    break;
  case 6:
    dayName = "Saturday";
    break;
  default: // Optional: handles cases not explicitly listed
    dayName = "Weekday";
}
console.log(`Today is ${dayName}`);
3.2 循环语句 (for, while, do...while, for...in, for...of)
for: 重复执行代码块固定次数。while: 当条件为真时重复执行代码块。do...while: 先执行一次代码块,然后当条件为真时重复。for...in: 遍历对象的可枚举属性 (键)。(不推荐用于数组遍历)for...of(ES6): 遍历可迭代对象 (如 Array, String, Map, Set) 的值。
// for loop
console.log("For loop:");
for (let i = 0; i < 3; i++) {
  console.log(i); // 0, 1, 2
}
// while loop
console.log("While loop:");
let count = 0;
while (count < 3) {
  console.log(count); // 0, 1, 2
  count++;
}
// do...while loop
console.log("Do...while loop:");
let j = 0;
do {
  console.log(j); // 0, 1, 2
  j++;
} while (j < 3);
// for...in (for object properties)
console.log("For...in loop (object):");
const car = { make: "Toyota", model: "Camry", year: 2021 };
for (let key in car) {
  // It's good practice to check if the property is directly on the object
  if (Object.hasOwnProperty.call(car, key)) {
    console.log(`${key}: ${car[key]}`); // make: Toyota, model: Camry, year: 2021
  }
}
// for...of (for iterable values - arrays, strings etc.)
console.log("For...of loop (array):");
const colors = ["red", "green", "blue"];
for (const color of colors) {
  console.log(color); // red, green, blue
}
console.log("For...of loop (string):");
const text = "abc";
for (const char of text) {
  console.log(char); // a, b, c
}
3.3 break 和 continue
break: 立即退出整个循环或switch语句。continue: 跳过当前循环的剩余部分,开始下一次迭代。
console.log("Break/Continue:");
for (let i = 0; i < 5; i++) {
  if (i === 3) {
    break; // Exit loop when i is 3
  }
  if (i === 1) {
    continue; // Skip iteration when i is 1
  }
  console.log(i); // 0, 2
}
4. 函数 (Functions)
函数是执行特定任务的代码块。
4.1 函数声明 (Function Declaration) vs 函数表达式 (Function Expression)
- 声明: 使用 
function关键字开头,会被提升 (hoisted)。 - 表达式: 将匿名或命名函数赋值给变量,不会被提升(变量声明会提升,但赋值不会)。
 
// Function Declaration (Hoisted)
console.log(declaredFunc(5, 3)); // 8 (can call before declaration)
function declaredFunc(a, b) {
  return a + b;
}
// Function Expression (Not hoisted in terms of assignment)
// console.log(expressedFunc(5, 3)); // TypeError: expressedFunc is not a function (or ReferenceError if using let/const due to TDZ)
const expressedFunc = function (a, b) {
  // Anonymous function expression
  return a * b;
};
console.log(expressedFunc(5, 3)); // 15
const namedExpressedFunc = function multiply(a, b) {
  // Named function expression (name 'multiply' primarily useful for debugging/recursion)
  return a * b;
};
console.log(namedExpressedFunc(5, 3)); // 15
// console.log(multiply(5,3)); // ReferenceError: multiply is not defined (name is local to the function scope)
4.2 箭头函数 (Arrow Functions - ES6)
提供更简洁的语法,并且不绑定自己的 this (词法 this)。
// Traditional function expression
const addTrad = function (a, b) {
  return a + b;
};
// Arrow function equivalent
const addArrow = (a, b) => {
  return a + b;
};
// Concise body (implicit return)
const subtract = (a, b) => a - b;
// Single parameter (parentheses optional)
const square = (x) => x * x;
// No parameters
const getRandom = () => Math.random();
console.log(addArrow(2, 3)); // 5
console.log(subtract(5, 2)); // 3
console.log(square(4)); // 16
console.log(getRandom()); // Random number
// 'this' behavior difference
function CounterTrad() {
  this.count = 0;
  // In traditional function, 'this' inside setInterval callback is Window/global or undefined (strict mode)
  // Need to use .bind(this), self = this, or arrow function
  /*
    setInterval(function() {
        this.count++; // 'this' is not the CounterTrad instance here
        console.log("Trad (problematic):", this.count);
    }, 1000);
    */
}
function CounterArrow() {
  this.count = 0;
  // Arrow function inherits 'this' from the surrounding lexical scope (CounterArrow)
  setInterval(() => {
    this.count++;
    console.log("Arrow:", this.count); // 'this' correctly refers to the CounterArrow instance
  }, 2000); // Use different interval to see output clearly
}
// const c1 = new CounterTrad(); // Would demonstrate the 'this' issue
const c2 = new CounterArrow(); // Works as expected
4.3 参数 (Parameters) 与 arguments 对象
- 默认参数 (ES6): 
function greet(name = "Guest") {...} - 剩余参数 (Rest Parameters - ES6): 
function sum(...numbers) {...}- 将多余的参数收集到一个数组中。 - arguments 对象: 类数组对象,包含函数调用时传递的所有参数 (在箭头函数中不可用)。尽量使用剩余参数代替。
 
// Default parameters
function greet(name = "Guest") {
  console.log(`Hello, ${name}!`);
}
greet(); // Hello, Guest!
greet("Alice"); // Hello, Alice!
// Rest parameters
function sum(...numbers) {
  let total = 0;
  for (const num of numbers) {
    total += num;
  }
  return total;
}
console.log(sum(1, 2, 3)); // 6
console.log(sum(10, 20, 30, 40)); // 100
// arguments object (less common in modern JS)
function logArgs() {
  console.log("Arguments object:", arguments);
  console.log("First arg:", arguments[0]);
  // return arguments.reduce(...); // Error: arguments.reduce is not a function (it's array-like, not a real array)
  // Convert to array if needed: Array.from(arguments) or [...arguments]
}
logArgs("a", "b", "c"); // Arguments object: [Arguments] { '0': 'a', '1': 'b', '2': 'c' }
// First arg: a
4.4 作用域 (Scope) 与 闭包 (Closure)
- 作用域: 变量和函数的可访问范围。
- 全局作用域 (Global Scope)
 - 函数作用域 (Function Scope)
 - 块级作用域 (Block Scope - 
let,const) 
 - 闭包: 一个函数能够“记住”并访问其词法作用域(创建该函数时的作用域),即使该函数在其词法作用域之外执行。
 
// Scope Example
let globalVar = "I'm global";
function outerFunc() {
  let outerVar = "I'm outer";
  function innerFunc() {
    let innerVar = "I'm inner";
    console.log(globalVar); // Access global
    console.log(outerVar); // Access outer scope
    console.log(innerVar); // Access local scope
  }
  innerFunc();
  // console.log(innerVar); // ReferenceError: innerVar is not defined
}
outerFunc();
// console.log(outerVar); // ReferenceError: outerVar is not defined
// Closure Example
function makeCounter() {
  let count = 0; // 'count' is captured by the inner function's closure
  return function () {
    // This inner function is the closure
    count++;
    console.log(count);
  };
}
const counter1 = makeCounter();
const counter2 = makeCounter();
counter1(); // 1
counter1(); // 2 (count is preserved within counter1's closure)
counter2(); // 1 (counter2 has its own independent closure and count)
counter1(); // 3
4.5 this 关键字
this 的值在函数调用时确定,取决于函数的调用方式。
- 全局上下文: 
this指向全局对象 (window在浏览器,global在 Node.js,严格模式下是undefined)。 - 函数调用:
- 普通函数调用: 
func()-this指向全局对象 (非严格模式) 或undefined(严格模式)。 - 作为对象方法调用: 
obj.method()-this指向调用该方法的对象 (obj)。 - 构造函数调用: 
new Constructor(...)-this指向新创建的实例对象。 apply,call,bind: 显式设置this的值。
 - 普通函数调用: 
 - 箭头函数: 不绑定自己的 
this,它会捕获其所在上下文(词法作用域)的this值。 
console.log(this === window); // true (in browser, non-module context)
function showThisStrict() {
  "use strict";
  console.log("Strict mode function 'this':", this); // undefined
}
showThisStrict();
const myObj = {
  name: "My Object",
  logName: function () {
    // Method
    console.log("Method 'this'.name:", this.name); // My Object
  },
  logNameArrow: () => {
    // Arrow function as property
    // 'this' here refers to the 'this' where myObj was defined (likely window/global)
    console.log("Arrow func 'this'.name:", this ? this.name : this); // undefined (or window.name if set)
  },
  delayedLog: function () {
    // Problem: 'this' inside setTimeout callback is not myObj
    setTimeout(function () {
      console.log(
        "Delayed Trad (problematic) 'this'.name:",
        this ? this.name : this
      ); // undefined or window.name
    }, 50);
    // Solution 1: Arrow function
    setTimeout(() => {
      console.log("Delayed Arrow 'this'.name:", this.name); // My Object
    }, 100);
    // Solution 2: bind
    setTimeout(
      function () {
        console.log("Delayed Bind 'this'.name:", this.name); // My Object
      }.bind(this),
      150
    );
  },
};
myObj.logName();
myObj.logNameArrow();
myObj.delayedLog();
// Constructor call
function Person(name) {
  this.name = name;
  console.log("Constructor 'this':", this); // Person { name: 'Alice' }
}
const p = new Person("Alice");
// call, apply, bind
function greetPerson(greeting, punctuation) {
  console.log(`${greeting}, ${this.name}${punctuation}`);
}
const personData = { name: "Bob" };
greetPerson.call(personData, "Hello", "!"); // Hello, Bob!
greetPerson.apply(personData, ["Hi", "."]); // Hi, Bob.
const greetBob = greetPerson.bind(personData, "Hey"); // Creates a new function with 'this' bound
greetBob("?"); // Hey, Bob?
5. 数据结构 (Data Structures)
5.1 对象 (Objects)
键值对的无序集合。键通常是字符串 (或 Symbol)。
- 创建: 字面量 
{},new Object()。 - 访问属性: 点表示法 
obj.property,方括号表示法obj['property'](键可以是变量或非标准标识符)。 - 添加/修改属性: 直接赋值 
obj.newProp = value。 - 删除属性: 
delete obj.prop。 - 遍历: 
for...in,Object.keys(),Object.values(),Object.entries()(ES8)。 
// Object literal
const student = {
  name: "Charlie",
  age: 20,
  "major subject": "Computer Science", // Key with space needs quotes
  greet: function () {
    console.log(`Hi, I'm ${this.name}`);
  },
};
// Accessing properties
console.log(student.name); // Charlie
console.log(student["age"]); // 20
console.log(student["major subject"]); // Computer Science
// Adding/Modifying
student.grade = "A";
student.age = 21;
console.log(student);
// Deleting
delete student.grade;
console.log(student);
// Methods
student.greet(); // Hi, I'm Charlie
// Iteration
console.log("Keys:", Object.keys(student)); // ["name", "age", "major subject", "greet"]
console.log("Values:", Object.values(student)); // ["Charlie", 21, "Computer Science", ƒ]
console.log("Entries:", Object.entries(student)); // [ ["name", "Charlie"], ... ]
console.log("Using for...in:");
for (const key in student) {
  if (Object.hasOwnProperty.call(student, key)) {
    console.log(`${key} -> ${student[key]}`);
  }
}
5.2 数组 (Arrays)
有序的元素列表。
- 创建: 字面量 
[],new Array(). - 访问元素: 通过索引 
arr[index](从 0 开始)。 - 长度: 
arr.length. - 常用方法:
- 修改原数组: 
push(),pop(),shift(),unshift(),splice(),sort(),reverse(). - 不修改原数组 (返回新数组): 
slice(),concat(),map(),filter(),reduce(),join(). - 遍历: 
forEach(),for...of. 
 - 修改原数组: 
 
// Array literal
const fruits = ["Apple", "Banana", "Orange"];
// Accessing
console.log(fruits[1]); // Banana
console.log(fruits.length); // 3
// Modifying methods
fruits.push("Mango"); // ["Apple", "Banana", "Orange", "Mango"]
console.log("After push:", fruits);
let lastFruit = fruits.pop(); // Removes "Mango"
console.log("Popped:", lastFruit, "Array:", fruits); // Popped: Mango Array: ["Apple", "Banana", "Orange"]
fruits.unshift("Strawberry"); // Adds to beginning
console.log("After unshift:", fruits); // ["Strawberry", "Apple", "Banana", "Orange"]
let firstFruit = fruits.shift(); // Removes "Strawberry"
console.log("Shifted:", firstFruit, "Array:", fruits); // Shifted: Strawberry Array: ["Apple", "Banana", "Orange"]
// splice(start, deleteCount, item1, item2, ...)
fruits.splice(1, 1, "Kiwi", "Peach"); // Remove 1 element at index 1, add "Kiwi", "Peach"
console.log("After splice:", fruits); // ["Apple", "Kiwi", "Peach", "Orange"]
// Non-modifying methods
const citrus = fruits.slice(2); // ["Peach", "Orange"] (from index 2 to end)
console.log("Slice:", citrus, "Original:", fruits); // Original is unchanged
const moreFruits = fruits.concat(["Grape", "Lemon"]);
console.log("Concat:", moreFruits);
const upperFruits = fruits.map((fruit) => fruit.toUpperCase());
console.log("Map:", upperFruits); // ["APPLE", "KIWI", "PEACH", "ORANGE"]
const longFruits = fruits.filter((fruit) => fruit.length > 5);
console.log("Filter:", longFruits); // ["Orange"]
const numbers = [1, 2, 3, 4];
const sumOfNums = numbers.reduce(
  (accumulator, currentValue) => accumulator + currentValue,
  0
);
console.log("Reduce:", sumOfNums); // 10
// Iteration
console.log("forEach:");
fruits.forEach((fruit, index) => {
  console.log(`${index}: ${fruit}`);
});
6. 面向对象编程 (OOP)
6.1 基于原型的继承 (Prototype-based Inheritance)
JavaScript 对象有一个指向其原型对象的内部链接。当试图访问一个对象的属性时,如果在该对象上找不到,就会沿着原型链向上查找。
// Constructor function (pre-ES6 common pattern)
function Animal(name) {
  this.name = name;
}
// Add method to the prototype (shared by all instances)
Animal.prototype.speak = function () {
  console.log(`${this.name} makes a sound.`);
};
const genericAnimal = new Animal("Generic");
genericAnimal.speak(); // Generic makes a sound.
// Inheriting from Animal
function Dog(name, breed) {
  Animal.call(this, name); // Call parent constructor to set 'name' on the Dog instance
  this.breed = breed;
}
// Set up the prototype chain: Dog.prototype inherits from Animal.prototype
// Object.create is preferred over new Animal() to avoid running Animal constructor unnecessarily
Dog.prototype = Object.create(Animal.prototype);
// Restore the constructor property (optional but good practice)
Dog.prototype.constructor = Dog;
// Add Dog-specific method or override
Dog.prototype.speak = function () {
  // Override
  console.log(`${this.name} barks.`);
};
Dog.prototype.wagTail = function () {
  console.log(`${this.name} wags tail.`);
};
const myDog = new Dog("Buddy", "Golden Retriever");
myDog.speak(); // Buddy barks. (Own method)
myDog.wagTail(); // Buddy wags tail. (Own method)
console.log(myDog.name); // Buddy (Set by Animal.call)
console.log(myDog instanceof Dog); // true
console.log(myDog instanceof Animal); // true (due to prototype chain)
6.2 class 语法 (ES6 Syntactic Sugar)
ES6 引入了 class 关键字,提供了一种更清晰、更熟悉的语法来创建对象和实现继承,但底层仍然是基于原型的。
class声明类。constructor方法用于初始化实例。extends关键字用于继承。super关键字用于调用父类的构造函数和方法。static关键字定义静态方法和属性(属于类本身,而非实例)。get和set定义 getter 和 setter。
class Vehicle {
  constructor(make, model) {
    this.make = make;
    this.model = model;
    this._serialNumber = Math.random().toString(16).slice(2); // Example private-like property
  }
  // Method
  getInfo() {
    return `${this.make} ${this.model}`;
  }
  // Static method (called on the class itself)
  static compareVehicles(v1, v2) {
    return v1.make === v2.make && v1.model === v2.model;
  }
  // Getter
  get serialNumber() {
    console.log("Getting serial number");
    return this._serialNumber;
  }
  // Setter (usually includes validation or logic)
  set serialNumber(value) {
    console.log("Attempting to set serial number (not allowed)");
    // Example: throw new Error("Serial number cannot be changed");
  }
}
class Car extends Vehicle {
  constructor(make, model, numDoors) {
    super(make, model); // Call parent constructor
    this.numDoors = numDoors;
  }
  // Override parent method
  getInfo() {
    return `${super.getInfo()} with ${this.numDoors} doors. Serial: ${
      this.serialNumber
    }`; // Call parent method + getter
  }
  // Car specific method
  honk() {
    console.log("Beep beep!");
  }
}
const myCar = new Car("Honda", "Civic", 4);
console.log(myCar.getInfo()); // Honda Civic with 4 doors. Serial: <random_hex>
myCar.honk(); // Beep beep!
// myCar.serialNumber = "new"; // Attempting to set serial number (not allowed)
const anotherCar = new Car("Honda", "Civic", 2);
console.log(Vehicle.compareVehicles(myCar, anotherCar)); // true (Using static method)
7. 异步编程 (Asynchronous Programming)
JavaScript 是单线程的,但通过事件循环、回调、Promises 和 async/await 可以处理耗时操作(如网络请求、文件 I/O、定时器)而不会阻塞主线程。
7.1 回调函数 (Callbacks)
将一个函数作为参数传递给另一个函数,在异步操作完成后调用。缺点是容易导致“回调地狱 (Callback Hell)”。
console.log("Start");
function fetchData(url, callback) {
  console.log(`Fetching data from ${url}...`);
  // Simulate network request delay
  setTimeout(() => {
    const data = { result: `Data from ${url}` };
    // Check for simulated error
    const error = url.includes("fail") ? new Error("Failed to fetch") : null;
    callback(error, data); // Call the callback with error or data
  }, 1000);
}
fetchData("api/users", (err, userData) => {
  if (err) {
    console.error("Error fetching users:", err.message);
    return;
  }
  console.log("Got user data:", userData);
  // Nested callback (potential callback hell)
  fetchData(`api/posts?userId=${userData.result}`, (err, postData) => {
    if (err) {
      console.error("Error fetching posts:", err.message);
      return;
    }
    console.log("Got post data:", postData);
  });
});
console.log("End (will log before callbacks finish)");
7.2 Promises (ES6)
Promise 对象表示一个异步操作的最终完成(或失败)及其结果值。它有三种状态: pending (进行中), fulfilled (已成功), rejected (已失败)。使用 .then() 处理成功,.catch() 处理失败,.finally() 处理无论成功或失败都要执行的操作。Promises 可以链式调用,改善了回调地狱。
console.log("Start Promise");
function fetchDataPromise(url) {
  return new Promise((resolve, reject) => {
    console.log(`Fetching (Promise) from ${url}...`);
    setTimeout(() => {
      if (url.includes("fail")) {
        reject(new Error(`Failed to fetch ${url}`));
      } else {
        resolve({ result: `Promise data from ${url}` });
      }
    }, 1000);
  });
}
fetchDataPromise("api/users")
  .then((userData) => {
    // Handle success of first promise
    console.log("Promise Got user data:", userData);
    // Return a new promise for chaining
    return fetchDataPromise(`api/posts?userId=${userData.result}`);
  })
  .then((postData) => {
    // Handle success of second promise
    console.log("Promise Got post data:", postData);
    // Example of returning a non-promise value for the next .then
    return "Processing complete";
  })
  .then((message) => {
    console.log("Final step:", message);
    // Simulate another failure
    return fetchDataPromise("api/comments?postId=fail");
  })
  .catch((error) => {
    // Handle any error in the chain
    console.error("Promise Error:", error.message);
  })
  .finally(() => {
    // Executes regardless of success or failure
    console.log("Promise operation finished.");
  });
console.log("End Promise (will log before promises resolve/reject)");
7.3 async/await (ES8/ES2017)
建立在 Promises 之上,提供了一种用同步风格编写异步代码的方式,使代码更易读。async 关键字用于声明一个异步函数,该函数隐式返回一个 Promise。await 关键字只能在 async 函数内部使用,它会暂停函数的执行,等待 Promise 被解决(fulfilled 或 rejected),然后恢复执行并返回解决的值(或抛出拒绝的原因)。
console.log("Start Async/Await");
// Re-use the fetchDataPromise function from the Promise example
async function processUserData() {
  try {
    console.log("Async: Getting user data...");
    const userData = await fetchDataPromise("api/users"); // Pauses here until promise resolves
    console.log("Async: Got user data:", userData);
    console.log("Async: Getting post data...");
    const postData = await fetchDataPromise(
      `api/posts?userId=${userData.result}`
    ); // Pauses here
    console.log("Async: Got post data:", postData);
    console.log("Async: Getting comments (will fail)...");
    const commentData = await fetchDataPromise("api/comments?postId=fail"); // Pauses and will throw error
    console.log("Async: Got comment data:", commentData); // This line won't be reached
    return "All data processed successfully"; // This becomes the resolved value of the promise returned by processUserData
  } catch (error) {
    console.error("Async/Await Error:", error.message);
    // Can re-throw or return an error indicator if needed
    return "Processing failed"; // This becomes the resolved value in case of error
  } finally {
    console.log("Async function finished attempt.");
  }
}
// Call the async function and handle its returned promise
processUserData()
  .then((result) => console.log("Async function final result:", result))
  .catch((err) => console.error("Unexpected error from async function:", err)); // Catch errors not handled inside
console.log("End Async/Await (will log before async function completes)");
8. DOM 操作 (Document Object Model)
在浏览器环境中,JavaScript 可以通过 DOM API 与 HTML 和 XML 文档进行交互。
8.1 选择元素
document.getElementById(id): 通过 ID 获取单个元素。document.getElementsByTagName(tagName): 通过标签名获取元素集合 (HTMLCollection)。document.getElementsByClassName(className): 通过类名获取元素集合 (HTMLCollection)。document.querySelector(selector): 通过 CSS 选择器获取匹配的第一个元素。document.querySelectorAll(selector): 通过 CSS 选择器获取所有匹配的元素 (NodeList)。
<!-- index.html -->
<!DOCTYPE html>
<html>
  <head>
    <title>DOM Example</title>
  </head>
  <body>
    <h1 id="main-title">Hello World</h1>
    <p class="content">This is a paragraph.</p>
    <p class="content">Another paragraph.</p>
    <ul>
      <li>Item 1</li>
      <li>Item 2</li>
    </ul>
    <button id="myButton">Click Me</button>
    <script src="script.js"></script>
  </body>
</html>
// script.js
// Get single elements
const titleElement = document.getElementById("main-title");
const firstParagraph = document.querySelector(".content"); // Gets the first element with class 'content'
const button = document.querySelector("#myButton"); // Using CSS selector for ID
// Get collections
const allParagraphsByClass = document.getElementsByClassName("content"); // HTMLCollection (live)
const allParagraphsBySelector = document.querySelectorAll(".content"); // NodeList (static)
const listItemsByTag = document.getElementsByTagName("li"); // HTMLCollection (live)
console.log(titleElement);
console.log(firstParagraph);
console.log(allParagraphsByClass); // HTMLCollection [ p.content, p.content ]
console.log(allParagraphsBySelector); // NodeList [ p.content, p.content ]
console.log(listItemsByTag); // HTMLCollection [ li, li ]
// Difference between HTMLCollection (live) and NodeList (static from querySelectorAll)
const list = document.querySelector("ul");
const liveListItems = list.getElementsByTagName("li"); // Live collection
const staticListItems = list.querySelectorAll("li"); // Static collection
console.log("Before Add:", liveListItems.length, staticListItems.length); // 2, 2
const newItem = document.createElement("li");
newItem.textContent = "Item 3";
list.appendChild(newItem);
console.log("After Add:", liveListItems.length, staticListItems.length); // 3, 2 (Live collection updated, Static did not)
8.2 修改元素内容和属性
element.innerHTML: 获取或设置元素的 HTML 内容。element.textContent: 获取或设置元素的文本内容 (更安全,不会解析 HTML)。element.style.property: 获取或设置 CSS 样式。element.setAttribute(name, value): 设置属性。element.getAttribute(name): 获取属性。element.removeAttribute(name): 移除属性。element.classList:add(),remove(),toggle(),contains()- 操作类名。
// script.js (continued)
// Change content
if (titleElement) {
  titleElement.textContent = "DOM Manipulation"; // Safer
  // titleElement.innerHTML = "<em>DOM</em> Manipulation"; // Allows HTML
}
// Change style
if (firstParagraph) {
  firstParagraph.style.color = "blue";
  firstParagraph.style.fontWeight = "bold";
}
// Attributes
const listElement = document.querySelector("ul");
if (listElement) {
  listElement.setAttribute("data-id", "fruit-list");
  console.log(listElement.getAttribute("data-id")); // fruit-list
}
// ClassList
if (titleElement) {
  titleElement.classList.add("important");
  titleElement.classList.remove("old-class"); // If it existed
  titleElement.classList.toggle("active"); // Add 'active' class
  titleElement.classList.toggle("active"); // Remove 'active' class
  console.log(titleElement.classList.contains("important")); // true
  console.log(titleElement.className); // The full class string
}
8.3 创建和插入/删除元素
document.createElement(tagName): 创建新元素。document.createTextNode(text): 创建文本节点。parentNode.appendChild(childNode): 将子节点添加到父节点的末尾。parentNode.insertBefore(newNode, referenceNode): 在参考节点之前插入新节点。parentNode.removeChild(childNode): 移除子节点。element.remove(): (较新) 直接移除元素自身。
// script.js (continued)
// Create new element
const newParagraph = document.createElement("p");
const textNode = document.createTextNode("This is a newly created paragraph.");
newParagraph.appendChild(textNode);
newParagraph.classList.add("content", "new");
newParagraph.style.backgroundColor = "#eee";
// Append to body
document.body.appendChild(newParagraph);
// Insert before the button
const theButton = document.getElementById("myButton");
if (theButton) {
  const anotherPara = document.createElement("p");
  anotherPara.textContent = "Inserted before button.";
  theButton.parentNode.insertBefore(anotherPara, theButton);
}
// Remove an element
const secondParagraph = document.querySelectorAll(".content")[1]; // The "Another paragraph."
if (secondParagraph) {
  // secondParagraph.parentNode.removeChild(secondParagraph); // Old way
  secondParagraph.remove(); // Simpler new way
}
9. 事件处理 (Event Handling)
响应用户交互(点击、鼠标移动、键盘输入等)或浏览器事件(页面加载完成、窗口大小改变等)。
9.1 添加事件监听器
element.addEventListener(eventType, handlerFunction, useCapture): 推荐方式。eventType: 事件名称 (如 "click", "mouseover", "keydown")。handlerFunction: 事件触发时执行的函数。useCapture: 布尔值,指定是在捕获阶段 (true) 还是冒泡阶段 (false, 默认) 处理事件。
element.on<eventtype> = handlerFunction: (旧方式) 如onclick,onmouseover。一次只能附加一个处理程序。
9.2 事件对象 (Event Object)
事件处理函数通常会接收一个事件对象作为参数,包含事件的详细信息。
event.type: 事件类型。event.target: 触发事件的原始元素。event.currentTarget: 当前正在处理事件的元素 (在事件冒泡时可能不同于target)。event.preventDefault(): 阻止事件的默认行为 (如阻止表单提交或链接跳转)。event.stopPropagation(): 阻止事件冒泡到父元素。event.key(键盘事件),event.keyCode(键盘事件, 不推荐)。event.clientX,event.clientY(鼠标事件)。
// script.js (continued)
const clickButton = document.getElementById("myButton");
const mainTitle = document.getElementById("main-title");
function handleButtonClick(event) {
  console.log("Button clicked!");
  console.log("Event Type:", event.type); // click
  console.log("Target Element:", event.target); // <button id="myButton">...</button>
  console.log("Current Target:", event.currentTarget); // <button id="myButton">...</button>
  mainTitle.textContent = `Button clicked at ${new Date().toLocaleTimeString()}`;
  // event.preventDefault(); // Not needed for button click unless it's type="submit" in a form
}
function handleTitleMouseover(event) {
  console.log("Mouse is over the title!");
  event.target.style.color = "red";
}
function handleTitleMouseout(event) {
  event.target.style.color = ""; // Reset color
}
if (clickButton) {
  // Preferred way: addEventListener
  clickButton.addEventListener("click", handleButtonClick);
  // Old way (overwrites previous onclick handlers if any)
  // clickButton.onclick = handleButtonClick;
}
if (mainTitle) {
  mainTitle.addEventListener("mouseover", handleTitleMouseover);
  mainTitle.addEventListener("mouseout", handleTitleMouseout);
}
// Event Propagation Example (Bubbling)
document.body.addEventListener("click", function (event) {
  console.log(`Body clicked! Target was: ${event.target.tagName}`);
  // If button is clicked, this will log after handleButtonClick logs,
  // because the event "bubbles up" from the button to the body.
});
// To stop bubbling from the button:
/*
clickButton.addEventListener('click', function(event) {
    handleButtonClick(event);
    event.stopPropagation(); // Prevents the body's click handler from firing
});
*/
9.3 事件委托 (Event Delegation)
将事件监听器添加到父元素,利用事件冒泡来处理子元素的事件。适用于子元素是动态添加或数量很多的情况,可以提高性能并简化代码。
<!-- index.html (add this inside body) -->
<ul id="item-list">
  <li>Item A</li>
  <li>Item B</li>
  <li>Item C</li>
</ul>
<button id="addItemBtn">Add Item</button>
// script.js (continued)
const itemList = document.getElementById("item-list");
const addItemBtn = document.getElementById("addItemBtn");
if (itemList) {
  // Attach ONE listener to the parent UL
  itemList.addEventListener("click", function (event) {
    // Check if the clicked element (event.target) is an LI
    if (event.target && event.target.nodeName === "LI") {
      console.log(`List item clicked: ${event.target.textContent}`);
      event.target.style.textDecoration = "line-through"; // Example action
    }
  });
}
if (addItemBtn) {
  let itemCount = 3;
  addItemBtn.addEventListener("click", function () {
    itemCount++;
    const newItem = document.createElement("li");
    newItem.textContent = `Item ${String.fromCharCode(65 + itemCount - 1)}`; // A, B, C, D...
    itemList.appendChild(newItem);
    // No need to add a new event listener to the new LI,
    // the parent listener handles it via delegation.
  });
}
10. ES6+ 主要特性 (Modern JavaScript Features)
除了上面已涉及的 let/const, Arrow Functions, Classes, Promises, for...of 等,还有:
10.1 模板字面量 (Template Literals)
使用反引号 (`) 定义字符串,可以方便地嵌入变量 ( ${expression}) 和创建多行字符串。
const userName = "Alice";
const userAge = 30;
// Old way
const greetingOld =
  "Hello, my name is " +
  userName +
  " and I am " +
  userAge +
  " years old.\nThis is a new line.";
// Template literal
const greetingNew = `Hello, my name is ${userName} and I am ${userAge} years old.
This is a new line. Calculation: ${10 + 5}`;
console.log(greetingOld);
console.log(greetingNew);
10.2 解构赋值 (Destructuring Assignment)
从数组或对象中提取值并赋给变量的简洁语法。
// Array Destructuring
const coordinates = [10, 20, 30];
const [x, y, z] = coordinates;
console.log(x, y, z); // 10 20 30
const [a, , c] = coordinates; // Skip an element
console.log(a, c); // 10 30
const [p, ...rest] = coordinates; // Rest element
console.log(p, rest); // 10 [ 20, 30 ]
// Object Destructuring
const userProfile = {
  id: 123,
  displayName: "Bob",
  email: "bob@example.com",
  address: {
    street: "123 Main St",
    city: "Anytown",
  },
};
const { displayName, email } = userProfile;
console.log(displayName, email); // Bob bob@example.com
// Rename variables
const { id: userId, email: userEmail } = userProfile;
console.log(userId, userEmail); // 123 bob@example.com
// Default values
const { displayName: name = "Guest", phone = "N/A" } = userProfile;
console.log(name, phone); // Bob N/A
// Nested destructuring
const {
  address: { city },
} = userProfile;
console.log(city); // Anytown
// Function parameter destructuring
function printUserInfo({ displayName, email }) {
  console.log(`User: ${displayName}, Email: ${email}`);
}
printUserInfo(userProfile); // User: Bob, Email: bob@example.com
10.3 展开语法 (Spread Syntax) 与 剩余语法 (Rest Syntax)
... 语法根据上下文有不同作用。
- 展开 (Spread): 在需要多个元素/参数/属性的地方,将可迭代对象(数组、字符串)或对象展开。
- 数组字面量: 
[...arr1, ...arr2] - 函数调用: 
myFunction(...argsArray) - 对象字面量 (ES2018): 
{ ...obj1, ...obj2, key: 'override' } 
 - 数组字面量: 
 - 剩余 (Rest): 在函数参数或解构赋值中,将剩余的元素/属性收集到一个数组或对象中。 (已在函数参数和解构中展示)
 
// Spread Syntax
// Arrays
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combinedArr = [...arr1, 0, ...arr2];
console.log(combinedArr); // [ 1, 2, 3, 0, 4, 5, 6 ]
const arrCopy = [...arr1]; // Shallow copy
arrCopy.push(4);
console.log(arr1); // [ 1, 2, 3 ] (original unchanged)
console.log(arrCopy); // [ 1, 2, 3, 4 ]
// Function calls
function multiplyThree(a, b, c) {
  return a * b * c;
}
const numsToMultiply = [2, 3, 4];
console.log(multiplyThree(...numsToMultiply)); // 24
// Objects (ES2018)
const objA = { a: 1, b: 2 };
const objB = { b: 3, c: 4 };
const combinedObj = { ...objA, ...objB, d: 5 }; // { a: 1, b: 3, c: 4, d: 5 } (objB.b overrides objA.b)
console.log(combinedObj);
const objACopy = { ...objA }; // Shallow copy
objACopy.a = 100;
console.log(objA); // { a: 1, b: 2 }
console.log(objACopy); // { a: 100, b: 2 }
10.4 模块 (Modules - ES6 Imports/Exports)
将代码组织成可重用的、独立的文件。
export: 导出函数、类、变量等。- 命名导出: 
export const name = ...;/export function func() {}/export { var1, func2 }; - 默认导出: 
export default ...;(每个模块只能有一个) 
- 命名导出: 
 import: 导入其他模块导出的内容。- 导入命名导出: 
import { name1, name2 } from './module.js'; - 导入命名导出并重命名: 
import { name1 as newName } from './module.js'; - 导入所有命名导出到一个对象: 
import * as myModule from './module.js'; - 导入默认导出: 
import myDefaultExport from './module.js'; - 混合导入: 
import myDefault, { named1, named2 } from './module.js'; 
- 导入命名导出: 
 
// --- utils.js ---
export const PI = 3.14159;
export function circumference(radius) {
  return 2 * PI * radius;
}
export default function greetModule(name = "Module User") {
  console.log(`Hello from the module, ${name}!`);
}
const secretValue = "Internal only"; // Not exported
// --- main.js ---
// Assuming utils.js is in the same directory
// Note: In browsers, you need <script type="module" src="main.js"></script>
import greetModule, {
  PI,
  circumference as calcCircumference,
} from "./utils.js";
// OR import * as utils from './utils.js';
console.log(PI); // 3.14159
// console.log(circumference(10)); // If using named import without alias
console.log(calcCircumference(10)); // 62.8318 (Using alias)
greetModule(); // Hello from the module, Module User!
greetModule("Alice"); // Hello from the module, Alice!
// If using namespace import:
// console.log(utils.PI);
// console.log(utils.circumference(10));
// utils.default(); // Calling default export via namespace
11. 错误处理 (Error Handling)
11.1 try...catch...finally
try: 包含可能抛出错误的代码。catch: 如果try块中发生错误,则执行此块,接收错误对象。finally: 无论是否发生错误,都会执行此块 (用于清理资源等)。
11.2 throw
手动抛出一个错误(可以是任何值,但通常是 Error 对象或其子类的实例)。
function divide(a, b) {
  if (b === 0) {
    throw new Error("Division by zero is not allowed."); // Throw an error object
  }
  if (typeof a !== "number" || typeof b !== "number") {
    throw new TypeError("Both arguments must be numbers.");
  }
  return a / b;
}
try {
  console.log("Attempting division...");
  let result1 = divide(10, 2);
  console.log("Result 1:", result1); // 5
  let result2 = divide(8, 0); // This will throw an error
  console.log("Result 2:", result2); // This line won't execute
} catch (error) {
  // Catches the thrown error
  console.error("An error occurred:");
  console.error("Name:", error.name); // e.g., "Error" or "TypeError"
  console.error("Message:", error.message); // The string passed to the Error constructor
  // console.error("Stack:", error.stack);    // Stack trace (environment dependent)
} finally {
  console.log("Division attempt finished."); // Executes regardless of error
}
try {
  let result3 = divide("ten", 2); // Throws TypeError
  console.log("Result 3:", result3);
} catch (error) {
  console.error("Another error:", error.message);
} finally {
  console.log("Finished another attempt.");
}
console.log("Program continues after try...catch.");
12. JSON (JavaScript Object Notation)
轻量级的数据交换格式,易于人阅读和编写,也易于机器解析和生成。基于 JavaScript 对象字面量语法。
JSON.stringify(value): 将 JavaScript 值(对象、数组等)转换为 JSON 字符串。JSON.parse(text): 将 JSON 字符串解析为 JavaScript 值。
const dataObject = {
  id: 42,
  name: "Example Data",
  isActive: true,
  tags: ["tag1", "tag2"],
  metadata: null,
  process: function () {}, // Functions are ignored by JSON.stringify
};
// Convert JS object to JSON string
const jsonString = JSON.stringify(dataObject, null, 2); // null, 2 for pretty printing with 2 spaces indentation
console.log("JSON String:\n", jsonString);
/* Output:
JSON String:
 {
  "id": 42,
  "name": "Example Data",
  "isActive": true,
  "tags": [
    "tag1",
    "tag2"
  ],
  "metadata": null
}
*/
// Convert JSON string back to JS object
const parsedObject = JSON.parse(jsonString);
console.log("\nParsed Object:", parsedObject);
console.log(parsedObject.name); // Example Data
console.log(parsedObject.tags[0]); // tag1
// console.log(parsedObject.process); // undefined (function was not included in JSON)
13. Web APIs (Browser Environment)
浏览器提供了许多 API 供 JavaScript 调用。
- DOM API: (已覆盖) 操作文档结构。
 - Fetch API / XMLHttpRequest: 发送和接收网络请求。
 - LocalStorage / SessionStorage: 在浏览器中存储键值对数据。
 - Timers: 
setTimeout(),setInterval(),clearTimeout(),clearInterval(). - Geolocation API: 获取用户地理位置。
 - Canvas API: 绘制 2D 图形。
 - Web Workers: 在后台线程执行 JavaScript。
 - ...等等
 
// Fetch API Example (replaces older XMLHttpRequest)
const apiUrl = "https://jsonplaceholder.typicode.com/posts/1"; // Example public API
fetch(apiUrl)
  .then((response) => {
    if (!response.ok) {
      // Check for HTTP errors (e.g., 404, 500)
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return response.json(); // Parse the response body as JSON (returns a promise)
  })
  .then((data) => {
    console.log("Fetched Data:", data);
  })
  .catch((error) => {
    console.error("Fetch Error:", error);
  });
// LocalStorage Example
localStorage.setItem("username", "Alice");
localStorage.setItem("theme", "dark");
const storedUsername = localStorage.getItem("username");
console.log("Stored Username:", storedUsername); // Alice
localStorage.removeItem("theme");
// localStorage.clear(); // Removes all items
// SessionStorage has the same API but data persists only for the session duration
// Timers Example
console.log("Timer Start");
const timeoutId = setTimeout(() => {
  console.log("This message appears after 2 seconds.");
}, 2000);
let intervalCount = 0;
const intervalId = setInterval(() => {
  intervalCount++;
  console.log(`Interval message #${intervalCount}`);
  if (intervalCount >= 3) {
    clearInterval(intervalId); // Stop the interval
    console.log("Interval stopped.");
  }
}, 500);
// clearTimeout(timeoutId); // Uncomment to cancel the timeout before it fires