JavaScript
JavaScript 是 Web 的编程语言。
所有现代的 HTML 页面都可以使用 JavaScript。
JavaScript 非常容易学。

为什么学习 JavaScript?
JavaScript 是 web 开发人员必须学习的 3 门语言中的一门:
- HTML 定义了网页的内容
- CSS 描述了网页的布局
- JavaScript 控制了网页的行为
阅读本教程前,您需要了解的知识:
阅读本教程,您需要有以下基础:
JavaScript 简介
JavaScript 是互联网上最流行的脚本语言,这门语言可用于 HTML 和 web,更可广泛用于服务器、PC、笔记本电脑、平板电脑和智能手机等设备。
JavaScript 是脚本语言
JavaScript 是一种轻量级的编程语言。
JavaScript 是可插入 HTML 页面的编程代码。
JavaScript 插入 HTML 页面后,可由所有的现代浏览器执行。
JavaScript 基础笔记
1. 基础语法
1.1 变量声明
// var - 函数作用域,可重复声明(不推荐)
var name = "张三";
// let - 块级作用域,不可重复声明
let age = 25;
age = 26; // 可以修改
// const - 块级作用域,不可重新赋值(常量)
const PI = 3.14159;
// PI = 3.14; // 报错
// const 对象的属性可以修改
const person = { name: "李四" };
person.name = "王五"; // 可以
// person = {}; // 报错
1.2 数据类型
基本类型(Primitive Types)
// 1. Number - 数字
let num = 42;
let float = 3.14;
let negative = -10;
let infinity = Infinity;
let notANumber = NaN;
// 2. String - 字符串
let str1 = "双引号";
let str2 = '单引号';
let str3 = `模板字符串 ${num}`; // 反引号,支持插值
// 3. Boolean - 布尔值
let isTrue = true;
let isFalse = false;
// 4. Undefined - 未定义
let x;
console.log(x); // undefined
// 5. Null - 空值
let empty = null;
// 6. Symbol - 唯一标识符(ES6)
let sym = Symbol("描述");
// 7. BigInt - 大整数(ES2020)
let bigNum = 9007199254740991n;
引用类型(Reference Types)
// Object - 对象
let obj = { name: "张三", age: 25 };
// Array - 数组
let arr = [1, 2, 3, 4, 5];
// Function - 函数
function greet() {
console.log("Hello");
}
// Date - 日期
let now = new Date();
// RegExp - 正则表达式
let pattern = /abc/;
1.3 类型检查与转换
// typeof 检查类型
typeof 42; // "number"
typeof "hello"; // "string"
typeof true; // "boolean"
typeof undefined; // "undefined"
typeof null; // "object" (历史遗留bug)
typeof {}; // "object"
typeof []; // "object"
typeof function(){}; // "function"
// 类型转换
String(123); // "123"
Number("456"); // 456
Boolean(0); // false
parseInt("42px"); // 42
parseFloat("3.14"); // 3.14
// 隐式转换
"5" + 3; // "53" (字符串拼接)
"5" - 3; // 2 (数字运算)
!!"hello"; // true (转布尔)
2. 运算符
2.1 算术运算符
let a = 10, b = 3;
a + b; // 13 加
a - b; // 7 减
a * b; // 30 乘
a / b; // 3.333... 除
a % b; // 1 取模(余数)
a ** b; // 1000 幂运算(ES2016)
// 自增自减
let x = 5;
x++; // 后置自增,返回5,x变为6
++x; // 前置自增,x变为7,返回7
x--; // 后置自减
--x; // 前置自减
2.2 比较运算符
// 相等性
5 == "5"; // true (宽松相等,会类型转换)
5 === "5"; // false (严格相等,不转换类型)
5 != "5"; // false
5 !== "5"; // true
// 大小比较
10 > 5; // true
10 < 5; // false
10 >= 10; // true
10 <= 5; // false
2.3 逻辑运算符
// && 与(AND)
true && true; // true
true && false; // false
// || 或(OR)
true || false; // true
false || false; // false
// ! 非(NOT)
!true; // false
!false; // true
// 短路求值
let result = null || "默认值"; // "默认值"
let value = true && "返回这个"; // "返回这个"
// 空值合并运算符(ES2020)
let count = 0;
count ?? 10; // 0 (只有null/undefined才用默认值)
count || 10; // 10 (0被视为假值)
2.4 其他运算符
// 三元运算符
let age = 18;
let status = age >= 18 ? "成年" : "未成年";
// 可选链(ES2020)
let user = { address: { city: "北京" } };
user?.address?.city; // "北京"
user?.contact?.phone; // undefined (不报错)
// 展开运算符
let arr1 = [1, 2, 3];
let arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]
let obj1 = { a: 1, b: 2 };
let obj2 = { ...obj1, c: 3 }; // { a: 1, b: 2, c: 3 }
3. 控制流程
3.1 条件语句
// if...else
let score = 85;
if (score >= 90) {
console.log("优秀");
} else if (score >= 60) {
console.log("及格");
} else {
console.log("不及格");
}
// switch
let day = 3;
switch (day) {
case 1:
console.log("星期一");
break;
case 2:
console.log("星期二");
break;
case 3:
console.log("星期三");
break;
default:
console.log("其他");
}
3.2 循环语句
// for 循环
for (let i = 0; i < 5; i++) {
console.log(i); // 0, 1, 2, 3, 4
}
// 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...of 遍历数组
let arr = [10, 20, 30];
for (let value of arr) {
console.log(value); // 10, 20, 30
}
// for...in 遍历对象属性
let obj = { a: 1, b: 2, c: 3 };
for (let key in obj) {
console.log(key, obj[key]); // a 1, b 2, c 3
}
// break 和 continue
for (let i = 0; i < 10; i++) {
if (i === 3) continue; // 跳过本次循环
if (i === 7) break; // 终止循环
console.log(i);
}
4. 函数
4.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) return "除数不能为0";
return a / b;
};
// 单参数可省略括号
const square = x => x * x;
// 无参数
const greet = () => console.log("Hello");
4.2 函数参数
// 默认参数
function greet(name = "访客") {
return `你好,${name}`;
}
// 剩余参数
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
sum(1, 2, 3, 4); // 10
// 解构参数
function printUser({ name, age }) {
console.log(`${name}, ${age}岁`);
}
printUser({ name: "张三", age: 25 });
4.3 作用域与闭包
// 作用域
let global = "全局变量";
function outer() {
let outerVar = "外部变量";
function inner() {
let innerVar = "内部变量";
console.log(global); // 可访问
console.log(outerVar); // 可访问
console.log(innerVar); // 可访问
}
inner();
// console.log(innerVar); // 报错,无法访问
}
// 闭包
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
4.4 this 关键字
// 普通函数中的 this
function showThis() {
console.log(this); // 浏览器中是 window,严格模式下是 undefined
}
// 对象方法中的 this
const person = {
name: "张三",
greet: function() {
console.log(this.name); // "张三"
}
};
// 箭头函数没有自己的 this,继承外层
const obj = {
name: "李四",
greet: () => {
console.log(this.name); // undefined(继承全局)
},
sayHi: function() {
const inner = () => {
console.log(this.name); // "李四"(继承 sayHi 的 this)
};
inner();
}
};
// call, apply, bind 改变 this
function introduce(greeting) {
console.log(`${greeting}, 我是${this.name}`);
}
const user = { name: "王五" };
introduce.call(user, "你好"); // "你好, 我是王五"
introduce.apply(user, ["嗨"]); // "嗨, 我是王五"
const boundFunc = introduce.bind(user, "Hello");
boundFunc(); // "Hello, 我是王五"
5. 数组
5.1 数组基础
// 创建数组
let arr1 = [1, 2, 3, 4, 5];
let arr2 = new Array(5); // 长度为5的空数组
let arr3 = Array.of(1, 2, 3); // [1, 2, 3]
// 访问元素
arr1[0]; // 1
arr1[arr1.length - 1]; // 5
// 修改元素
arr1[0] = 10;
// 数组长度
arr1.length; // 5
5.2 数组方法
let arr = [1, 2, 3, 4, 5];
// 添加/删除元素
arr.push(6); // 末尾添加,返回新长度
arr.pop(); // 末尾删除,返回删除的元素
arr.unshift(0); // 开头添加
arr.shift(); // 开头删除
arr.splice(2, 1, 99); // 从索引2删除1个,插入99
// 查找元素
arr.indexOf(3); // 2(索引)
arr.includes(3); // true
arr.find(x => x > 3); // 4(第一个满足条件的元素)
arr.findIndex(x => x > 3); // 3(索引)
// 数组转换
arr.slice(1, 3); // [2, 3](截取,不改变原数组)
arr.concat([6, 7]); // [1,2,3,4,5,6,7](合并)
arr.join("-"); // "1-2-3-4-5"(转字符串)
arr.reverse(); // 反转(改变原数组)
arr.sort(); // 排序(改变原数组)
arr.sort((a, b) => a - b); // 数字升序
// 高阶方法
arr.forEach(x => console.log(x)); // 遍历
arr.map(x => x * 2); // [2,4,6,8,10]
arr.filter(x => x > 2); // [3,4,5]
arr.reduce((sum, x) => sum + x, 0); // 15(累加)
arr.every(x => x > 0); // true(所有元素满足)
arr.some(x => x > 4); // true(至少一个满足)
// ES6+ 方法
arr.flat(); // 数组扁平化
arr.flatMap(x => [x, x * 2]); // map + flat
Array.from("hello"); // ["h","e","l","l","o"]
5.3 数组解构
let [a, b, c] = [1, 2, 3];
console.log(a, b, c); // 1 2 3
// 跳过元素
let [first, , third] = [1, 2, 3];
// 剩余元素
let [head, ...tail] = [1, 2, 3, 4];
console.log(head, tail); // 1 [2,3,4]
// 默认值
let [x = 10, y = 20] = [5];
console.log(x, y); // 5 20
6. 对象
6.1 对象基础
// 创建对象
let obj1 = { name: "张三", age: 25 };
let obj2 = new Object();
obj2.name = "李四";
// 访问属性
obj1.name; // "张三"(点表示法)
obj1["age"]; // 25(方括号表示法)
// 添加/修改属性
obj1.city = "北京";
obj1.age = 26;
// 删除属性
delete obj1.city;
// 检查属性
"name" in obj1; // true
obj1.hasOwnProperty("name"); // true
// 属性简写(ES6)
let name = "王五", age = 30;
let person = { name, age }; // { name: "王五", age: 30 }
// 计算属性名
let key = "score";
let student = { [key]: 95 }; // { score: 95 }
// 方法简写
let obj = {
greet() {
console.log("Hello");
}
};
6.2 对象方法
let 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]]
// 合并对象
Object.assign({}, obj, { d: 4 }); // { a:1, b:2, c:3, d:4 }
let merged = { ...obj, d: 4 }; // 展开运算符(推荐)
// 冻结对象
Object.freeze(obj); // 不可修改
Object.seal(obj); // 不可添加/删除属性,可修改现有属性
// 对象转换
JSON.stringify(obj); // '{"a":1,"b":2,"c":3}'
JSON.parse('{"a":1}'); // { a: 1 }
6.3 对象解构
let person = { name: "张三", age: 25, city: "北京" };
// 基本解构
let { name, age } = person;
// 重命名
let { name: userName, age: userAge } = person;
// 默认值
let { name, country = "中国" } = person;
// 剩余属性
let { name, ...rest } = person;
console.log(rest); // { age: 25, city: "北京" }
// 嵌套解构
let user = {
info: { name: "李四", age: 30 },
address: { city: "上海" }
};
let { info: { name }, address: { city } } = user;
7. 字符串
7.1 字符串方法
let str = "Hello World";
// 长度
str.length; // 11
// 访问字符
str[0]; // "H"
str.charAt(0); // "H"
str.charCodeAt(0); // 72(字符编码)
// 查找
str.indexOf("o"); // 4
str.lastIndexOf("o"); // 7
str.includes("World"); // true
str.startsWith("Hello"); // true
str.endsWith("World"); // true
// 提取
str.slice(0, 5); // "Hello"
str.substring(0, 5); // "Hello"
str.substr(0, 5); // "Hello"(已废弃)
// 替换
str.replace("World", "JS"); // "Hello JS"
str.replaceAll("o", "0"); // "Hell0 W0rld"
// 大小写
str.toLowerCase(); // "hello world"
str.toUpperCase(); // "HELLO WORLD"
// 去空格
" hello ".trim(); // "hello"
" hello ".trimStart(); // "hello "
" hello ".trimEnd(); // " hello"
// 分割/连接
str.split(" "); // ["Hello", "World"]
["Hello", "World"].join(" "); // "Hello World"
// 重复/填充
"abc".repeat(3); // "abcabcabc"
"5".padStart(3, "0"); // "005"
"5".padEnd(3, "0"); // "500"
7.2 模板字符串
let name = "张三";
let age = 25;
// 基本用法
let message = `你好,${name}!你今年${age}岁。`;
// 多行字符串
let html = `
<div>
<h1>${name}</h1>
<p>年龄:${age}</p>
</div>
`;
// 表达式
let result = `1 + 1 = ${1 + 1}`;
// 标签模板
function highlight(strings, ...values) {
return strings.reduce((result, str, i) => {
return result + str + (values[i] ? `<strong>${values[i]}</strong>` : "");
}, "");
}
let output = highlight`姓名:${name},年龄:${age}`;
8. ES6+ 新特性
8.1 解构赋值
// 数组解构
let [a, b] = [1, 2];
// 对象```markdown
// 对象解构
let { name, age } = { name: "张三", age: 25 };
// 函数参数解构
function greet({ name, age }) {
console.log(`${name}, ${age}岁`);
}
8.2 展开运算符
// 数组展开
let arr1 = [1, 2, 3];
let arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]
// 对象展开
let obj1 = { a: 1, b: 2 };
let obj2 = { ...obj1, c: 3 }; // { a: 1, b: 2, c: 3 }
// 函数参数展开
Math.max(...[1, 5, 3]); // 5
// 复制数组/对象(浅拷贝)
let arrCopy = [...arr1];
let objCopy = { ...obj1 };
8.3 Promise 与异步
// Promise 基础
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("成功");
// reject("失败");
}, 1000);
});
promise
.then(result => console.log(result))
.catch(error => console.error(error))
.finally(() => console.log("完成"));
// Promise 链式调用
fetch("https://api.example.com/data")
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
// Promise 静态方法
Promise.all([promise1, promise2]) // 全部成功才成功
.then(results => console.log(results));
Promise.race([promise1, promise2]) // 第一个完成的结果
.then(result => console.log(result));
Promise.allSettled([promise1, promise2]) // 等待全部完成
.then(results => console.log(results));
Promise.any([promise1, promise2]) // 第一个成功的结果
.then(result => console.log(result));
// async/await(ES2017)
async function fetchData() {
try {
let response = await fetch("https://api.example.com/data");
let data = await response.json();
console.log(data);
return data;
} catch (error) {
console.error("错误:", error);
}
}
// 并行执行
async function parallel() {
let [result1, result2] = await Promise.all([
fetch("/api/1").then(r => r.json()),
fetch("/api/2").then(r => r.json())
]);
}
8.4 Class 类
// 类定义
class Person {
// 构造函数
constructor(name, age) {
this.name = name;
this.age = age;
}
// 实例方法
greet() {
console.log(`你好,我是${this.name}`);
}
// 静态方法
static create(name, age) {
return new Person(name, age);
}
// Getter
get info() {
return `${this.name}, ${this.age}岁`;
}
// Setter
set info(value) {
[this.name, this.age] = value.split(",");
}
}
// 使用类
let person = new Person("张三", 25);
person.greet();
console.log(person.info);
// 继承
class Student extends Person {
constructor(name, age, grade) {
super(name, age); // 调用父类构造函数
this.grade = grade;
}
// 重写方法
greet() {
super.greet(); // 调用父类方法
console.log(`我是${this.grade}年级学生`);
}
}
let student = new Student("李四", 18, 12);
student.greet();
// 私有字段(ES2022)
class BankAccount {
#balance = 0; // 私有字段
deposit(amount) {
this.#balance += amount;
}
getBalance() {
return this.#balance;
}
}
8.5 模块化
// 导出(export)
// math.js
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export default function multiply(a, b) {
return a * b;
}
// 导入(import)
// main.js
import multiply, { PI, add } from "./math.js";
import * as math from "./math.js"; // 导入全部
console.log(PI);
console.log(add(2, 3));
console.log(multiply(2, 3));
console.log(math.PI);
// 动态导入
async function loadModule() {
const module = await import("./math.js");
console.log(module.PI);
}
8.6 其他新特性
// Map 和 Set
let map = new Map();
map.set("name", "张三");
map.set("age", 25);
map.get("name"); // "张三"
map.has("age"); // true
map.delete("age");
map.size; // 1
let set = new Set([1, 2, 3, 3, 4]);
console.log(set); // Set {1, 2, 3, 4}
set.add(5);
set.has(3); // true
set.delete(3);
set.size; // 4
// Symbol
let sym1 = Symbol("描述");
let sym2 = Symbol("描述");
sym1 === sym2; // false(每个Symbol都是唯一的)
let obj = {
[sym1]: "值"
};
// 迭代器和生成器
function* generator() {
yield 1;
yield 2;
yield 3;
}
let gen = generator();
gen.next(); // { value: 1, done: false }
gen.next(); // { value: 2, done: false }
gen.next(); // { value: 3, done: false }
gen.next(); // { value: undefined, done: true }
// for...of 遍历生成器
for (let value of generator()) {
console.log(value); // 1, 2, 3
}
// Proxy 代理
let target = { name: "张三" };
let proxy = new Proxy(target, {
get(target, prop) {
console.log(`读取 ${prop}`);
return target[prop];
},
set(target, prop, value) {
console.log(`设置 ${prop} = ${value}`);
target[prop] = value;
return true;
}
});
proxy.name; // 读取 name
proxy.age = 25; // 设置 age = 25
// Reflect
Reflect.get(target, "name");
Reflect.set(target, "age", 25);
Reflect.has(target, "name");
Reflect.deleteProperty(target, "age");
9. DOM 操作
9.1 选择元素
// 单个元素
document.getElementById("myId");
document.querySelector(".myClass");
document.querySelector("#myId");
// 多个元素
document.getElementsByClassName("myClass");
document.getElementsByTagName("div");
document.querySelectorAll(".myClass");
9.2 修改元素
let element = document.querySelector("#myDiv");
// 修改内容
element.textContent = "纯文本";
element.innerHTML = "<strong>HTML内容</strong>";
// 修改属性
element.setAttribute("data-id", "123");
element.getAttribute("data-id");
element.removeAttribute("data-id");
// 修改样式
element.style.color = "red";
element.style.backgroundColor = "blue";
element.classList.add("active");
element.classList.remove("hidden");
element.classList.toggle("visible");
element.classList.contains("active");
// 修改数据属性
element.dataset.userId = "123";
console.log(element.dataset.userId);
9.3 创建和删除元素
// 创建元素
let newDiv = document.createElement("div");
newDiv.textContent = "新元素";
newDiv.className = "box";
// 添加元素
let parent = document.querySelector("#parent");
parent.appendChild(newDiv); // 末尾添加
parent.insertBefore(newDiv, parent.firstChild); // 开头添加
parent.append(newDiv); // 末尾添加(可添加多个)
parent.prepend(newDiv); // 开头添加
// 删除元素
newDiv.remove(); // 删除自己
parent.removeChild(newDiv); // 父元素删除子元素
// 替换元素
parent.replaceChild(newDiv, oldDiv);
9.4 事件处理
let button = document.querySelector("#myButton");
// 添加事件监听
button.addEventListener("click", function(event) {
console.log("按钮被点击");
console.log(event.target); // 触发事件的元素
event.preventDefault(); // 阻止默认行为
event.stopPropagation(); // 阻止事件冒泡
});
// 移除事件监听
function handleClick(event) {
console.log("点击");
}
button.addEventListener("click", handleClick);
button.removeEventListener("click", handleClick);
// 常见事件
element.addEventListener("click", handler); // 点击
element.addEventListener("dblclick", handler); // 双击
element.addEventListener("mouseenter", handler); // 鼠标进入
element.addEventListener("mouseleave", handler); // 鼠标离开
element.addEventListener("keydown", handler); // 键盘按下
element.addEventListener("keyup", handler); // 键盘抬起
element.addEventListener("submit", handler); // 表单提交
element.addEventListener("input", handler); // 输入变化
element.addEventListener("change", handler); // 值改变
element.addEventListener("focus", handler); // 获得焦点
element.addEventListener("blur", handler); // 失去焦点
// 事件委托
document.querySelector("#list").addEventListener("click", function(event) {
if (event.target.tagName === "LI") {
console.log("点击了列表项:", event.target.textContent);
}
});
10. 常见操作
10.1 定时器
// setTimeout - 延迟执行一次
let timeoutId = setTimeout(() => {
console.log("3秒后执行");
}, 3000);
// 取消定时器
clearTimeout(timeoutId);
// setInterval - 重复执行
let intervalId = setInterval(() => {
console.log("每2秒执行一次");
}, 2000);
// 取消定时器
clearInterval(intervalId);
// 倒计时示例
let count = 10;
let countdown = setInterval(() => {
console.log(count);
count--;
if (count < 0) {
clearInterval(countdown);
console.log("倒计时结束");
}
}, 1000);
10.2 本地存储
// localStorage - 永久存储
localStorage.setItem("username", "张三");
localStorage.getItem("username"); // "张三"
localStorage.removeItem("username");
localStorage.clear(); // 清空所有
// 存储对象
let user = { name: "张三", age: 25 };
localStorage.setItem("user", JSON.stringify(user));
let savedUser = JSON.parse(localStorage.getItem("user"));
// sessionStorage - 会话存储(关闭标签页后清除)
sessionStorage.setItem("token", "abc123");
sessionStorage.getItem("token");
10.3 错误处理
// try...catch
try {
let result = riskyOperation();
console.log(result);
} catch (error) {
console.error("发生错误:", error.message);
} finally {
console.log("无论如何都会执行");
}
// 抛出错误
function divide(a, b) {
if (b === 0) {
throw new Error("除数不能为0");
}
return a / b;
}
// 自定义错误
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
throw new ValidationError("验证失败");
// 全局错误处理
window.addEventListener("error", function(event) {
console.error("全局错误:", event.error);
});
// Promise 错误处理
window.addEventListener("unhandledrejection", function(event) {
console.error("未处理的Promise拒绝:", event.reason);
});
10.4 正则表达式
// 创建正则
let regex1 = /abc/;
let regex2 = new RegExp("abc");
// 测试匹配
/hello/.test("hello world"); // true
// 查找匹配
"hello world".match(/o/g); // ["o", "o"]
"hello world".search(/world/); // 6(索引)
// 替换
"hello world".replace(/o/g, "0"); // "hell0 w0rld"
// 分割
"a,b,c".split(/,/); // ["a", "b", "c"]
// 常用模式
let email = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/;
let phone = /^1[3-9]\d{9}$/;
let url = /^https?:\/\/.+/;
let number = /^\d+$/;
// 捕获组
let dateRegex = /(\d{4})-(\d{2})-(\d{2})/;
let match = "2024-01-15".match(dateRegex);
console.log(match[1], match[2], match[3]); // 2024 01 15
// 命名捕获组
let regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
let result = "2024-01-15".match(regex);
console.log(result.groups.year); // 2024
10.5 常用工具函数
// 防抖(debounce)
function debounce(func, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(this, args), delay);
};
}
// 使用
let search = debounce(function(query) {
console.log("搜索:", query);
}, 500);
// 节流(throttle)
function throttle(func, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// 深拷贝
function deepClone(obj) {
if (obj === null || typeof obj !== "object") return obj;
if (obj instanceof Date) return new Date(obj);
if (obj instanceof Array) return obj.map(item => deepClone(item));
let cloned = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
cloned[key] = deepClone(obj[key]);
}
}
return cloned;
}
// 简单版(不处理特殊情况)
let deepCopy = JSON.parse(JSON.stringify(obj));
// 数组去重
let unique1 = [...new Set(array)];
let unique2 = array.filter((item, index) => array.indexOf(item) === index);
// 数组扁平化
let flat1 = array.flat(Infinity);
let flat2 = array.reduce((acc, val) =>
Array.isArray(val) ? acc.concat(flat2(val)) : acc.concat(val), []);
// 随机数
function random(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// 格式化日期
function formatDate(date) {
let year = date.getFullYear();
let month = String(d```markdown
let month = String(date.getMonth() + 1).padStart(2, "0");
let day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
}
// 判断数据类型
function getType(value) {
return Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
}
getType([]); // "array"
getType({}); // "object"
getType(null); // "null"
11. 常见概念
11.1 变量提升
// var 会提升
console.log(x); // undefined(不报错)
var x = 5;
// 等价于
var x;
console.log(x);
x = 5;
// let/const 不会提升(暂时性死区)
console.log(y); // 报错:Cannot access 'y' before initialization
let y = 10;
// 函数声明会提升
greet(); // "Hello"(可以调用)
function greet() {
console.log("Hello");
}
// 函数表达式不会提升
sayHi(); // 报错
const sayHi = function() {
console.log("Hi");
};
11.2 值类型 vs 引用类型
// 值类型(基本类型)- 复制值
let a = 10;
let b = a;
b = 20;
console.log(a); // 10(a 不变)
// 引用类型 - 复制引用(地址)
let obj1 = { name: "张三" };
let obj2 = obj1;
obj2.name = "李四";
console.log(obj1.name); // "李四"(obj1 也变了)
// 比较
let arr1 = [1, 2, 3];
let arr2 = [1, 2, 3];
arr1 == arr2; // false(比较引用地址)
arr1 === arr2; // false
// 函数参数
function change(num, obj) {
num = 100; // 不影响外部
obj.name = "王五"; // 影响外部
}
let n = 10;
let person = { name: "张三" };
change(n, person);
console.log(n); // 10
console.log(person.name); // "王五"
11.3 作用域链
let global = "全局";
function outer() {
let outerVar = "外层";
function inner() {
let innerVar = "内层";
console.log(innerVar); // 找到:内层
console.log(outerVar); // 向上找:外层
console.log(global); // 继续向上:全局
}
inner();
}
outer();
11.4 事件循环(Event Loop)
console.log("1");
setTimeout(() => {
console.log("2");
}, 0);
Promise.resolve().then(() => {
console.log("3");
});
console.log("4");
// 输出顺序:1, 4, 3, 2
// 解释:
// 1. 同步代码先执行:1, 4
// 2. 微任务(Promise):3
// 3. 宏任务(setTimeout):2
11.5 严格模式
"use strict";
// 严格模式下的限制:
// 1. 变量必须声明
x = 10; // 报错
// 2. 禁止删除变量
let y = 20;
delete y; // 报错
// 3. 函数参数不能重名
function test(a, a) {} // 报错
// 4. this 不会指向全局对象
function show() {
console.log(this); // undefined(非严格模式是 window)
}
// 5. 禁止八进制字面量
let num = 010; // 报错
12. 常见面试题
12.1 数据类型判断
// typeof 的局限性
typeof null; // "object"(bug)
typeof []; // "object"
typeof function(){}; // "function"
// 准确判断
Object.prototype.toString.call([]); // "[object Array]"
Object.prototype.toString.call(null); // "[object Null]"
Array.isArray([]); // true
12.2 == vs ===
// == 会类型转换
5 == "5"; // true
0 == false; // true
null == undefined; // true
// === 严格相等
5 === "5"; // false
0 === false; // false
null === undefined; // false
// 推荐使用 ===
12.3 浅拷贝 vs 深拷贝
let original = { a: 1, b: { c: 2 } };
// 浅拷贝 - 只复制第一层
let shallow1 = { ...original };
let shallow2 = Object.assign({}, original);
shallow1.a = 10; // 不影响 original
shallow1.b.c = 20; // 影响 original(嵌套对象共享引用)
// 深拷贝 - 完全独立
let deep1 = JSON.parse(JSON.stringify(original));
// 注意:无法处理函数、undefined、Symbol、循环引用
// 手动深拷贝
function deepClone(obj) {
if (obj === null || typeof obj !== "object") return obj;
let clone = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key]);
}
}
return clone;
}
12.4 闭包应用
// 1. 数据私有化
function createCounter() {
let count = 0;
return {
increment() { return ++count; },
decrement() { return --count; },
getCount() { return count; }
};
}
let counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
// count 无法直接访问
// 2. 循环中的闭包问题
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// 输出:3, 3, 3(var 是函数作用域)
// 解决方法1:使用 let
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// 输出:0, 1, 2
// 解决方法2:立即执行函数
for (var i = 0; i < 3; i++) {
(function(j) {
setTimeout(() => console.log(j), 100);
})(i);
}
12.5 数组常见操作
let arr = [1, 2, 3, 4, 5];
// 求和
let sum = arr.reduce((acc, val) => acc + val, 0);
// 求最大值
let max = Math.max(...arr);
// 数组去重
let unique = [...new Set(arr)];
// 数组扁平化
let nested = [1, [2, [3, [4]]]];
let flat = nested.flat(Infinity);
// 数组分组
let numbers = [1, 2, 3, 4, 5, 6];
let grouped = numbers.reduce((acc, num) => {
let key = num % 2 === 0 ? "even" : "odd";
(acc[key] = acc[key] || []).push(num);
return acc;
}, {});
// { odd: [1,3,5], even: [2,4,6] }
// 数组交集
let arr1 = [1, 2, 3];
let arr2 = [2, 3, 4];
let intersection = arr1.filter(x => arr2.includes(x));
// 数组差集
let difference = arr1.filter(x => !arr2.includes(x));
12.6 对象常见操作
let obj = { a: 1, b: 2, c: 3 };
// 对象遍历
for (let key in obj) {
console.log(key, obj[key]);
}
Object.keys(obj).forEach(key => {
console.log(key, obj[key]);
});
// 对象过滤
let filtered = Object.fromEntries(
Object.entries(obj).filter(([key, value]) => value > 1)
);
// { b: 2, c: 3 }
// 对象映射
let mapped = Object.fromEntries(
Object.entries(obj).map(([key, value]) => [key, value * 2])
);
// { a: 2, b: 4, c: 6 }
// 合并对象
let obj1 = { a: 1, b: 2 };
let obj2 = { b: 3, c: 4 };
let merged = { ...obj1, ...obj2 }; // { a: 1, b: 3, c: 4 }
13. 最佳实践
13.1 命名规范
// 变量和函数:小驼峰
let userName = "张三";
function getUserInfo() {}
// 常量:全大写下划线
const MAX_COUNT = 100;
const API_URL = "https://api.example.com";
// 类:大驼峰
class UserService {}
// 私有变量:下划线开头(约定)
let _privateVar = "私有";
// 布尔值:is/has/can 开头
let isActive = true;
let hasPermission = false;
let canEdit = true;
13.2 代码风格
// 使用 const/let,避免 var
const name = "张三";
let age = 25;
// 使用 === 而不是 ==
if (value === 10) {}
// 使用模板字符串
let message = `你好,${name}`;
// 使用箭头函数
let double = x => x * 2;
// 使用解构
let { name, age } = user;
let [first, second] = array;
// 使用展开运算符
let newArr = [...oldArr, newItem];
let newObj = { ...oldObj, newProp: value };
// 提前返回,减少嵌套
function process(data) {
if (!data) return;
if (!data.valid) return;
// 处理逻辑
}
13.3 性能优化
// 1. 避免全局变量
(function() {
let localVar = "局部变量";
})();
// 2. 缓存 DOM 查询
let element = document.querySelector("#myDiv");
// 多次使用 element,而不是重复查询
// 3. 事件委托
document.querySelector("#list").addEventListener("click", function(e) {
if (e.target.tagName === "LI") {
// 处理点击
}
});
// 4. 防抖和节流
let search = debounce(searchFunction, 300);
// 5. 使用文档片段
let fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
let li = document.createElement("li");
fragment.appendChild(li);
}
list.appendChild(fragment); // 一次性添加
// 6. 避免内存泄漏
// 移除事件监听
element.removeEventListener("click", handler);
// 清除定时器
clearInterval(intervalId);
总结
JavaScript 基础知识点:
- 变量与数据类型:let/const、7种基本类型、引用类型
- 运算符:算术、比较、逻辑、三元
- 控制流程:if/else、switch、for/while
- 函数:声明、表达式、箭头函数、作用域、闭包
- 数组:创建、方法、遍历、高阶函数
- 对象:创建、属性、方法、解构
- 字符串:方法、模板字符串
- ES6+:解构、展开、Promise、async/await、Class、模块化
- DOM 操作:选择、修改、创建、事件
- 常用工具:定时器、存储、错误处理、正则
学习建议:
- 多写代码,多练习
- 理解概念,不要死记硬背
- 从简单到复杂,循序渐进
- 查阅 MDN 文档深入学习
- 做小项目巩固知识
这份笔记涵盖了 JavaScript 的核心基础知识,适合初学者系统学习和快速查阅! 最后更新 · 2026-05-19 23:52