JavaScript 作为 Web 开发的核心语言,从简单的表单验证发展到如今支持前端框架、服务端、移动端、桌面应用的全能语言,已经成为开发者必须掌握的技能。本文将全面系统地梳理 JavaScript 的核心知识点,帮助初学者建立完整的知识体系,也为有经验的开发者提供查漏补缺的参考。
一、JavaScript 基础
1.1 语言概述
JavaScript 是一种动态类型、弱类型、基于原型的脚本语言。它是 ECMAScript 规范的实现,目前主流的版本是 ES6(ES2015)及后续的年度更新。
三大核心组成部分:
ECMAScript:语言核心,定义语法、类型、语句等
DOM:文档对象模型,操作页面元素
BOM:浏览器对象模型,操作浏览器窗口
1.2 变量与声明
// var —— 函数作用域,存在变量提升
var name = "张三";
console.log(name); // 输出:张三
// let —— 块级作用域,无变量提升
let age = 25;
age = 26; // 可重新赋值
// const —— 块级作用域,声明常量(引用类型内部可修改)
const PI = 3.14159;
// PI = 3.14; // 报错:常量不可重新赋值
// 引用类型常量可修改属性
const person = { name: "李四" };
person.name = "王五"; // 允许
变量提升详解:
console.log(a); // undefined,不会报错
var a = 10;
// 相当于:
var a;
console.log(a);
a = 10;
1.3 数据类型
JavaScript 有 8 种数据类型(7 种原始类型 + 1 种引用类型):
原始类型(存储在栈内存):
// Number —— 整数和浮点数
let num = 42;
let floatNum = 3.14;
let infinity = Infinity;
let notANumber = NaN; // 非数字
// String —— 字符串
let str1 = '单引号';
let str2 = "双引号";
let str3 = `模板字符串`; // ES6,支持插值
// Boolean —— 布尔值
let isTrue = true;
let isFalse = false;
// Undefined —— 已声明未赋值
let undef;
console.log(undef); // undefined
// Null —— 空值
let empty = null;
// Symbol —— ES6,唯一值
let sym1 = Symbol('id');
let sym2 = Symbol('id');
console.log(sym1 === sym2); // false
// BigInt —— ES2020,大整数
let bigNum = 9007199254740991n;
引用类型(存储在堆内存,栈存地址):
// Object
let obj = { name: "对象", age: 18 };
// Array
let arr = [1, 2, 3, 4, 5];
// Function
function fn() { return "函数"; }
// Date
let date = new Date();
// RegExp
let reg = /[a-z]+/g;
类型检测:
typeof 42; // "number"
typeof "hello"; // "string"
typeof true; // "boolean"
typeof undefined; // "undefined"
typeof null; // "object" —— 著名的语言缺陷
typeof Symbol(); // "symbol"
typeof {}; // "object"
typeof []; // "object"
typeof function(){}; // "function"
// 更准确的检测
Array.isArray([]); // true
Object.prototype.toString.call([]); // "[object Array]"
1.4 类型转换
隐式转换(发生在运算符操作时):
// 字符串拼接
"5" + 3; // "53"
5 + "3"; // "53"
5 + 3 + "2"; // "82"(先算术运算,再拼接)
// 算术运算
"5" - 3; // 2
"5" * "2"; // 10
"hello" - 1; // NaN
// 布尔值转换
if ("hello") { } // 非空字符串 → true
if (0) { } // 0 → false
if ([]) { } // 空数组 → true
if ({}) { } // 空对象 → true
显式转换:
// 转换为数字
Number("123"); // 123
Number("123abc"); // NaN
parseInt("123px"); // 123
parseFloat("3.14"); // 3.14
+"123"; // 123(一元运算符)
// 转换为字符串
String(123); // "123"
(123).toString(); // "123"
123 + ""; // "123"
// 转换为布尔值
Boolean(0); // false
Boolean(""); // false
Boolean(null); // false
Boolean(undefined); // false
Boolean(NaN); // false
!!"hello"; // true(双重取反)
1.5 运算符
// 算术运算符
+ - * / % ** // ** 指数运算(ES7)
// 赋值运算符
= += -= *= /= %= **=
// 比较运算符
== === != !== > < >= <=
// 严格相等 vs 宽松相等
1 == "1"; // true(类型转换后比较)
1 === "1"; // false(类型不同直接返回 false)
null == undefined; // true
null === undefined; // false
// 逻辑运算符
&& || ! // 与、或、非
// 短路运算
let user = null;
let name = user && user.name; // null,避免报错
let port = config.port || 3000; // 默认值
// 三元运算符
let status = age >= 18 ? "成年" : "未成年";
// 空值合并运算符(ES2020)
let value = input ?? "默认值"; // 只有 null/undefined 时使用默认值
// 可选链操作符(ES2020)
let userName = user?.profile?.name; // 安全访问深层属性
1.6 流程控制
// if...else
if (condition) {
// 代码块
} else if (anotherCondition) {
// 代码块
} else {
// 代码块
}
// switch
switch (value) {
case 1:
console.log("一");
break;
case 2:
console.log("二");
break;
default:
console.log("其他");
}
// 循环
// for 循环
for (let i = 0; i < 5; i++) {
console.log(i);
}
// while 循环
let i = 0;
while (i < 5) {
console.log(i);
i++;
}
// do...while 循环(至少执行一次)
let j = 0;
do {
console.log(j);
j++;
} while (j < 5);
// for...in(遍历对象可枚举属性)
for (let key in obj) {
console.log(key, obj[key]);
}
// for...of(遍历可迭代对象)
for (let item of arr) {
console.log(item);
}
// break 和 continue
for (let i = 0; i < 10; i++) {
if (i === 5) break; // 跳出循环
if (i % 2 === 0) continue; // 跳过本次迭代
console.log(i);
}
二、函数与作用域
2.1 函数定义
// 函数声明(存在提升)
function add(a, b) {
return a + b;
}
// 函数表达式
const subtract = function(a, b) {
return a - b;
};
// 箭头函数(ES6)
const multiply = (a, b) => a * b;
const divide = (a, b) => {
if (b === 0) throw new Error("除数不能为零");
return a / b;
};
// 立即执行函数表达式(IIFE)
(function() {
console.log("立即执行");
})();
(() => {
console.log("箭头函数 IIFE");
})();
2.2 参数处理
// 默认参数(ES6)
function greet(name = "匿名") {
return `你好,${name}`;
}
// 剩余参数(ES6)
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
sum(1, 2, 3, 4); // 10
// arguments 对象(类数组,箭头函数中没有)
function oldSum() {
let total = 0;
for (let i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}
// 解构参数
function setUser({ name, age, email = "默认邮箱" }) {
console.log(name, age, email);
}
setUser({ name: "张三", age: 25 });
2.3 作用域与闭包
作用域类型:
全局作用域:在函数外部声明的变量
函数作用域:使用 var 声明的变量
块级作用域:使用 let/const 声明的变量
// 词法作用域(静态作用域)
let global = "全局";
function outer() {
let outerVar = "外部变量";
function inner() {
let innerVar = "内部变量";
console.log(outerVar); // 可访问外部变量
console.log(global); // 可访问全局变量
}
inner();
// console.log(innerVar); // 错误:不能访问内部变量
}
outer();
闭包(Closure):
// 闭包:函数能够记住并访问其词法作用域
function createCounter() {
let count = 0;
return {
increment: function() {
count++;
return count;
},
decrement: function() {
count--;
return count;
},
getCount: function() {
return count;
}
};
}
const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.decrement(); // 1
console.log(counter.getCount()); // 1
// 实际应用:数据私有化、函数工厂、模块模式
function createAdder(x) {
return function(y) {
return x + y;
};
}
const add5 = createAdder(5);
console.log(add5(3)); // 8
console.log(add5(10)); // 15
2.4 高阶函数
// 函数作为参数
function forEach(arr, callback) {
for (let i = 0; i < arr.length; i++) {
callback(arr[i], i, arr);
}
}
forEach([1, 2, 3], (item) => console.log(item));
// 函数作为返回值
function once(fn) {
let called = false;
return function(...args) {
if (!called) {
called = true;
return fn.apply(this, args);
}
};
}
const init = once(() => console.log("只执行一次"));
init(); // 输出
init(); // 不执行
2.5 递归
// 经典递归:斐波那契数列
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
// 尾递归优化(JavaScript 引擎支持有限)
function factorial(n, accumulator = 1) {
if (n <= 1) return accumulator;
return factorial(n - 1, n * accumulator);
}
// 递归遍历树形结构
const tree = {
name: "根节点",
children: [
{ name: "子节点1", children: [] },
{
name: "子节点2",
children: [
{ name: "孙节点", children: [] }
]
}
]
};
function traverse(node) {
console.log(node.name);
node.children.forEach(child => traverse(child));
}
traverse(tree);
三、对象与面向对象
3.1 对象创建方式
// 对象字面量
const person = {
name: "张三",
age: 25,
sayHello() {
console.log(`你好,我是${this.name}`);
}
};
// 使用 new Object()
const obj = new Object();
obj.key = "value";
// 工厂模式
function createPerson(name, age) {
return {
name,
age,
sayHello() {
console.log(`你好,我是${this.name}`);
}
};
}
// 构造函数模式
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHello = function() {
console.log(`你好,我是${this.name}`);
};
}
const p1 = new Person("张三", 25);
// 原型模式
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(`${this.name}发出叫声`);
};
// ES6 Class 语法
class Student {
constructor(name, grade) {
this.name = name;
this.grade = grade;
}
study() {
console.log(`${this.name}正在学习`);
}
}
3.2 原型与原型链
核心概念:
每个函数都有一个 prototype 属性
每个对象都有一个 proto 属性(内部属性 [[Prototype]])
实例的 proto 指向构造函数的 prototype
function Parent(name) {
this.name = name;
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
const child = new Parent("小明");
console.log(child.__proto__ === Parent.prototype); // true
console.log(Parent.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null
// 原型链查找
child.sayName(); // 在 child 上找不到,去 __proto__ 上找
// 原型链继承
function Child(name, age) {
Parent.call(this, name); // 继承属性
this.age = age;
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
Child.prototype.sayAge = function() {
console.log(this.age);
};
const c = new Child("小红", 10);
c.sayName(); // 小红
c.sayAge(); // 10
3.3 Class 语法详解(ES6+)
class Animal {
// 静态属性
static kingdom = "动物界";
// 实例属性(ES2022 新语法)
species = "未知";
constructor(name) {
this.name = name;
}
// 实例方法
speak() {
console.log(`${this.name}发出叫声`);
}
// 静态方法
static create(name) {
return new Animal(name);
}
// Getter
get description() {
return `这是一只${this.name}`;
}
// Setter
set setName(value) {
this.name = value;
}
}
// 继承
class Dog extends Animal {
constructor(name, breed) {
super(name); // 调用父类构造函数
this.breed = breed;
}
// 重写方法
speak() {
console.log(`${this.name}汪汪叫`);
}
// 调用父类方法
parentSpeak() {
super.speak();
}
}
const dog = new Dog("旺财", "金毛");
dog.speak(); // 旺财汪汪叫
dog.parentSpeak(); // 旺财发出叫声
console.log(dog.description); // 这是一只旺财
console.log(Animal.kingdom); // 动物界
3.4 对象方法扩展
// Object 静态方法
const obj = { a: 1, b: 2, c: 3 };
// 遍历对象
Object.keys(obj); // ["a", "b", "c"]
Object.values(obj); // [1, 2, 3]
Object.entries(obj); // [["a",1], ["b",2], ["c",3]]
// 对象复制与合并
const copy = Object.assign({}, obj);
const merged = Object.assign({}, obj, { d: 4, e: 5 });
// 浅拷贝的另一种方式
const shallowCopy = { ...obj };
// 冻结对象(不可修改)
const frozen = Object.freeze({ name: "不可变" });
Object.isFrozen(frozen); // true
// 密封对象(不可添加/删除属性)
const sealed = Object.seal({ name: "密封" });
Object.isSealed(sealed); // true
// 属性描述符
const desc = Object.getOwnPropertyDescriptor(obj, "a");
console.log(desc);
// { value: 1, writable: true, enumerable: true, configurable: true }
Object.defineProperty(obj, "d", {
value: 4,
writable: false,
enumerable: true,
configurable: false
});