基本语法
变量声明
// let - 可重新赋值
let name = "张三";
name = "李四";
// const - 常量,不可重新赋值
const PI = 3.14159;
// PI = 3.14; // 会报错
// var - 旧方式,不推荐使用
var age = 25;
数据类型
// 数字
let score = 95;
let price = 19.99;
// 字符串
let greeting = "Hello, World!";
let name = '小明';
// 布尔值
let isLogged = true;
let isEmpty = false;
// Null 和 Undefined
let emptyValue = null; // 表示空值
let notAssigned; // undefined,未赋值
// 类型查看
console.log(typeof score); // "number"
console.log(typeof greeting); // "string"
运算符
// 算术运算符
let a = 10, b = 3;
console.log(a + b); // 13
console.log(a - b); // 7
console.log(a * b); // 30
console.log(a / b); // 3.333...
console.log(a % b); // 1 (取余数)
// 比较运算符
console.log(10 > 5); // true
console.log(10 == 10); // true
console.log(10 != 5); // true
// 逻辑运算符
console.log(true && false); // false (与)
console.log(true || false); // true (或)
console.log(!true); // false (非)
流程控制
// if/else 语句
let temperature = 25;
if (temperature > 30) {
console.log("天气很热");
} else if (temperature > 20) {
console.log("天气舒适");
} else {
console.log("天气凉爽");
}
// for 循环
for (let i = 0; i < 5; i++) {
console.log("当前数字: " + i);
}
// while 循环
let count = 0;
while (count < 3) {
console.log("计数: " + count);
count++;
}
// for 循环中使用 break
for (let i = 1; i <= 10; i++) {
if (i === 5) {
console.log("找到5,停止循环");
break; // 立即退出整个循环
}
console.log("当前数字:", i);
}
// 输出:
// 当前数字: 1
// 当前数字: 2
// 当前数字: 3
// 当前数字: 4
// 找到5,停止循环
// while 循环中使用 break
let count = 1;
while (count <= 10) {
if (count > 5) {
console.log("超过5了,停止循环");
break;
}
console.log("计数:", count);
count++;
}
// 跳过偶数,只打印奇数
for (let i = 1; i <= 10; i++) {
if (i % 2 === 0) {
continue; // 跳过偶数,继续下一次循环
}
console.log("奇数:", i);
}
// 输出: 1, 3, 5, 7, 9
// 跳过特定条件的元素
let fruits = ["苹果", "香蕉", "橙子", "", "葡萄", null, "草莓"];
for (let i = 0; i < fruits.length; i++) {
if (!fruits[i]) { // 如果水果为空或null
continue; // 跳过无效数据
}
console.log("水果:", fruits[i]);
}
// 输出: 苹果, 香蕉, 橙子, 葡萄, 草莓
函数
函数声明与调用
// 函数声明
function sayHello(name) {
return "你好, " + name + "!";
}
// 函数调用
let message = sayHello("王五");
console.log(message); // "你好, 王五!"
// 函数表达式
const multiply = function(a, b) {
return a * b;
};
console.log(multiply(4, 5)); // 20
// 箭头函数 (ES6)
const add = (a, b) => a + b;
console.log(add(2, 3)); // 5
作用域
let globalVar = "我是全局变量"; // 全局作用域
function testScope() {
let localVar = "我是局部变量"; // 函数作用域
console.log(globalVar); // 可以访问全局变量
console.log(localVar); // 可以访问局部变量
}
testScope();
console.log(globalVar); // 可以访问
// console.log(localVar); // 报错!无法访问函数内的局部变量
对象与数组
####对象
// 创建对象
let person = {
name: "李雷",
age: 20,
isStudent: true,
sayHi: function() {
return "大家好,我是" + this.name;
}
};
// 访问对象属性
console.log(person.name); // "李雷"
console.log(person["age"]); // 20
console.log(person.sayHi()); // "大家好,我是李雷"
// 修改对象
person.age = 21;
person.city = "北京"; // 添加新属性
数组
// 创建数组
let fruits = ["苹果", "香蕉", "橙子"];
let numbers = [1, 2, 3, 4, 5];
// 访问数组元素
console.log(fruits[0]); // "苹果"
console.log(fruits[1]); // "香蕉"
// 数组长度
console.log(fruits.length); // 3
// 修改数组
fruits.push("葡萄"); // 末尾添加
fruits.pop(); // 删除最后一个
fruits.unshift("草莓"); // 开头添加
fruits.shift(); // 删除第一个
数组常用方法
let numbers = [1, 2, 3, 4, 5];
// map - 对每个元素执行函数并返回新数组
let doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// filter - 过滤符合条件的元素
let evenNumbers = numbers.filter(num => num % 2 === 0);
console.log(evenNumbers); // [2, 4]
// find - 查找第一个符合条件的元素
let firstEven = numbers.find(num => num % 2 === 0);
console.log(firstEven); // 2
// forEach - 遍历数组
numbers.forEach(num => console.log(num));
DOM 操作
选择元素
// 通过id选择
let title = document.getElementById("title");
// 通过类名选择(返回集合)
let items = document.getElementsByClassName("item");
// 通过CSS选择器选择(返回第一个)
let firstButton = document.querySelector(".btn");
// 通过CSS选择器选择(返回所有)
let allButtons = document.querySelectorAll(".btn");
修改元素
let element = document.getElementById("myElement");
// 修改内容
element.textContent = "新的文本内容";
element.innerHTML = "<strong>加粗文本</strong>";
// 修改样式
element.style.color = "red";
element.style.fontSize = "20px";
element.style.display = "none"; // 隐藏元素
// 修改属性
element.setAttribute("data-id", "123");
let id = element.getAttribute("data-id");
创建和添加元素
// 创建新元素
let newDiv = document.createElement("div");
newDiv.textContent = "我是新创建的div";
newDiv.className = "new-class";
// 添加到页面
let container = document.getElementById("container");
container.appendChild(newDiv);
// 插入到指定位置
let firstChild = container.firstChild;
container.insertBefore(newDiv, firstChild);
// 移除元素
container.removeChild(newDiv);
事件处理
添加事件监听器
let button = document.getElementById("myButton");
// 方法1:使用 addEventListener
button.addEventListener("click", function() {
console.log("按钮被点击了!");
this.style.backgroundColor = "blue";
});
// 方法2:使用 onclick(不推荐多个处理函数)
button.onclick = function() {
alert("按钮被点击了!");
};
常用事件类型
let input = document.getElementById("myInput");
// 键盘事件
input.addEventListener("keydown", function(event) {
console.log("按键按下:", event.key);
});
// 鼠标事件
let box = document.getElementById("myBox");
box.addEventListener("mouseover", function() {
this.style.backgroundColor = "yellow";
});
box.addEventListener("mouseout", function() {
this.style.backgroundColor = "white";
});
// 表单事件
let form = document.getElementById("myForm");
form.addEventListener("submit", function(event) {
event.preventDefault(); // 阻止表单默认提交
console.log("表单提交了!");
});
事件委托
// 在父元素上监听,处理动态添加的子元素
let list = document.getElementById("myList");
list.addEventListener("click", function(event) {
// 检查点击的是否是li元素
if (event.target.tagName === "LI") {
console.log("点击了:", event.target.textContent);
event.target.classList.toggle("selected");
}
});
BOM 基础
常用 BOM 对象
// 窗口操作
console.log(window.innerWidth); // 窗口宽度
console.log(window.innerHeight); // 窗口高度
// 定时器
let timer = setTimeout(function() {
console.log("3秒后执行");
}, 3000);
// 清除定时器
// clearTimeout(timer);
// 循环定时器
let counter = 0;
let interval = setInterval(function() {
console.log("每秒执行一次", counter);
counter++;
if (counter > 5) {
clearInterval(interval); // 停止循环
}
}, 1000);
浏览器信息
// 浏览器信息
console.log(navigator.userAgent); // 浏览器标识
console.log(navigator.language); // 浏览器语言
// 位置信息
console.log(location.href); // 完整URL
console.log(location.hostname); // 域名
console.log(location.pathname); // 路径
// 页面跳转
// location.href = "https://www.example.com";
练习
待办事项列表
// 简单的待办列表实现思路
let todoList = [];
function addTodo(task) {
todoList.push({ task: task, completed: false });
renderTodos();
}
function toggleTodo(index) {
todoList[index].completed = !todoList[index].completed;
renderTodos();
}
function renderTodos() {
let listElement = document.getElementById("todoList");
listElement.innerHTML = "";
todoList.forEach((todo, index) => {
let li = document.createElement("li");
li.textContent = todo.task;
if (todo.completed) {
li.style.textDecoration = "line-through";
}
li.addEventListener("click", () => toggleTodo(index));
listElement.appendChild(li);
});
}
进阶
深入函数与对象
闭包详解
// 闭包:函数能够访问并记住其词法作用域,即使函数在作用域外执行
function createCounter() {
let count = 0; // 私有变量
return {
increment: function() {
count++;
return count;
},
decrement: function() {
count--;
return count;
},
getCount: function() {
return count;
}
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.decrement()); // 1
console.log(counter.getCount()); // 1
// 无法直接访问 count,实现了数据私有化
// console.log(counter.count); // undefined
闭包的实际应用
// 创建具有私有状态的函数
function createBankAccount(initialBalance) {
let balance = initialBalance;
return {
deposit: function(amount) {
if (amount > 0) {
balance += amount;
return `存款成功,当前余额: ${balance}`;
}
return "存款金额必须大于0";
},
withdraw: function(amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
return `取款成功,当前余额: ${balance}`;
}
return "取款金额无效或余额不足";
},
getBalance: function() {
return balance;
}
};
}
const account = createBankAccount(1000);
console.log(account.deposit(500)); // 存款成功,当前余额: 1500
console.log(account.withdraw(200)); // 取款成功,当前余额: 1300
console.log(account.getBalance()); // 1300
this 关键字全面解析
// 1. 默认绑定(全局上下文)
function showThis() {
console.log(this); // 浏览器中为 window,Node.js 中为 global
}
showThis();
// 2. 隐式绑定(方法调用)
const person = {
name: "张三",
sayHello: function() {
console.log(`你好,我是${this.name}`);
}
};
person.sayHello(); // 你好,我是张三
// 3. 显式绑定(call, apply, bind)
function introduce(age, city) {
console.log(`我是${this.name},${age}岁,来自${city}`);
}
const user1 = { name: "李四" };
const user2 = { name: "王五" };
// call - 立即调用,参数逐个传递
introduce.call(user1, 25, "北京");
// apply - 立即调用,参数数组传递
introduce.apply(user2, [30, "上海"]);
// bind - 返回新函数,延迟调用
const boundIntroduce = introduce.bind(user1, 28);
boundIntroduce("广州");
箭头函数的 this
const obj = {
name: "测试对象",
regularFunction: function() {
console.log("普通函数this:", this); // 指向obj
},
arrowFunction: () => {
console.log("箭头函数this:", this); // 指向外层作用域(通常是window)
},
// 实际应用场景
delayedGreeting: function() {
// 问题:setTimeout中的this会丢失
setTimeout(function() {
console.log("普通函数:", this.name); // undefined
}, 100);
// 解决方案1:保存this
const self = this;
setTimeout(function() {
console.log("保存this:", self.name); // 测试对象
}, 200);
// 解决方案2:使用箭头函数(推荐)
setTimeout(() => {
console.log("箭头函数:", this.name); // 测试对象
}, 300);
}
};
obj.regularFunction();
obj.arrowFunction();
obj.delayedGreeting();
原型与原型链
原型基础
// 构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
// 在原型上添加方法(所有实例共享)
Person.prototype.sayHello = function() {
console.log(`你好,我是${this.name},今年${this.age}岁`);
};
Person.prototype.getBirthYear = function() {
const currentYear = new Date().getFullYear();
return currentYear - this.age;
};
// 创建实例
const person1 = new Person("张三", 25);
const person2 = new Person("李四", 30);
person1.sayHello(); // 你好,我是张三,今年25岁
person2.sayHello(); // 你好,我是李四,今年30岁
console.log(person1.getBirthYear()); // 根据当前年份计算
原型链
function Animal(name) {
this.name = name;
}
Animal.prototype.eat = function() {
console.log(`${this.name}正在吃东西`);
};
function Dog(name, breed) {
Animal.call(this, name); // 调用父类构造函数
this.breed = breed;
}
// 设置原型链继承
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
// 添加子类特有方法
Dog.prototype.bark = function() {
console.log(`${this.name}在汪汪叫`);
};
const myDog = new Dog("旺财", "金毛");
myDog.eat(); // 继承自Animal
myDog.bark(); // Dog自己的方法
// 原型链验证
console.log(myDog instanceof Dog); // true
console.log(myDog instanceof Animal); // true
console.log(myDog instanceof Object); // true
ES6 Class 语法
class Animal {
constructor(name) {
this.name = name;
}
eat() {
console.log(`${this.name}正在吃东西`);
}
sleep() {
console.log(`${this.name}正在睡觉`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // 调用父类构造函数
this.breed = breed;
}
bark() {
console.log(`${this.name}在汪汪叫`);
}
// 重写父类方法
eat() {
console.log(`${this.name}正在吃狗粮`);
}
}
const dog = new Dog("小黑", "拉布拉多");
dog.eat(); // 小黑正在吃狗粮
dog.bark(); // 小黑在汪汪叫
dog.sleep(); // 小黑正在睡觉
异步编程
Promise 深入理解
// Promise 三种状态:pending, fulfilled, rejected
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
const random = Math.random();
if (random > 0.5) {
resolve(`成功!随机数: ${random}`);
} else {
reject(`失败!随机数: ${random}`);
}
}, 1000);
});
promise
.then(result => {
console.log("成功:", result);
return "继续处理";
})
.then(secondResult => {
console.log("第二步:", secondResult);
})
.catch(error => {
console.error("错误:", error);
})
.finally(() => {
console.log("无论如何都会执行");
});
Promise 实用方法
// Promise.all - 所有Promise都成功
const promises1 = [
Promise.resolve("结果1"),
Promise.resolve("结果2"),
Promise.resolve("结果3")
];
Promise.all(promises1)
.then(results => {
console.log("all成功:", results); // ["结果1", "结果2", "结果3"]
})
.catch(error => {
console.error("all失败:", error);
});
// Promise.allSettled - 所有Promise都完成(无论成功失败)
const promises2 = [
Promise.resolve("成功1"),
Promise.reject("失败1"),
Promise.resolve("成功2")
];
Promise.allSettled(promises2)
.then(results => {
console.log("allSettled:", results);
// [
// { status: "fulfilled", value: "成功1" },
// { status: "rejected", reason: "失败1" },
// { status: "fulfilled", value: "成功2" }
// ]
});
// Promise.race - 第一个完成(无论成功失败)
const promises3 = [
new Promise(resolve => setTimeout(() => resolve("快"), 100)),
new Promise(resolve => setTimeout(() => resolve("慢"), 500))
];
Promise.race(promises3)
.then(result => {
console.log("race结果:", result); // "快"
});
Async/Await
// 模拟异步API调用
function fetchUserData(userId) {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ id: userId, name: `用户${userId}`, age: 20 + userId });
}, 1000);
});
}
function fetchUserPosts(userId) {
return new Promise((resolve) => {
setTimeout(() => {
resolve([`帖子1-用户${userId}`, `帖子2-用户${userId}`]);
}, 800);
});
}
// 使用 async/await
async function getUserInfo(userId) {
try {
console.log("开始获取用户信息...");
const userData = await fetchUserData(userId);
console.log("用户数据:", userData);
const userPosts = await fetchUserPosts(userId);
console.log("用户帖子:", userPosts);
return {
...userData,
posts: userPosts
};
} catch (error) {
console.error("获取用户信息失败:", error);
throw error;
}
}
// 调用async函数
getUserInfo(1)
.then(userInfo => {
console.log("完整用户信息:", userInfo);
})
.catch(error => {
console.error("最终错误:", error);
});
并行异步操作
async function getMultipleUsers(userIds) {
try {
// 并行执行多个异步操作
const userPromises = userIds.map(id => fetchUserData(id));
const users = await Promise.all(userPromises);
console.log("所有用户:", users);
return users;
} catch (error) {
console.error("获取多个用户失败:", error);
}
}
// 使用
getMultipleUsers([1, 2, 3, 4, 5]);
Event Loop 理解
console.log("1. 同步代码开始");
// 宏任务
setTimeout(() => {
console.log("6. setTimeout - 宏任务");
}, 0);
// 微任务
Promise.resolve().then(() => {
console.log("4. Promise - 微任务");
});
// 微任务
queueMicrotask(() => {
console.log("5. queueMicrotask - 微任务");
});
console.log("2. 同步代码结束");
// 同步代码(立即执行)
for (let i = 0; i < 3; i++) {
console.log(`3. 同步循环 ${i}`);
}
// 执行顺序:
// 1. 同步代码开始
// 2. 同步代码结束
// 3. 同步循环 0
// 3. 同步循环 1
// 3. 同步循环 2
// 4. Promise - 微任务
// 5. queueMicrotask - 微任务
// 6. setTimeout - 宏任务
ES6+ 新特性深入
解构赋值高级用法
// 对象解构
const user = {
id: 1,
personalInfo: {
name: "张三",
age: 25,
address: {
city: "北京",
street: "长安街"
}
},
hobbies: ["读书", "游泳", "编程"]
};
// 嵌套解构
const {
personalInfo: {
name,
age,
address: { city }
},
hobbies: [firstHobby, ...otherHobbies]
} = user;
console.log(name, age, city); // 张三 25 北京
console.log(firstHobby, otherHobbies); // 读书 ["游泳", "编程"]
// 函数参数解构
function createUser({ name, age = 18, ...rest }) {
return {
username: name.toLowerCase(),
age,
metadata: rest
};
}
const newUser = createUser({
name: "李四",
email: "lisi@example.com",
city: "上海"
});
console.log(newUser);
模板字符串标签函数
// 标签函数 - 自定义模板字符串处理
function highlight(strings, ...values) {
let result = "";
strings.forEach((string, i) => {
result += string;
if (i < values.length) {
result += `<mark>${values[i]}</mark>`;
}
});
return result;
}
const name = "张三";
const score = 95;
const highlighted = highlight`学生 ${name} 的分数是 ${score} 分`;
console.log(highlighted);
// 学生 <mark>张三</mark> 的分数是 <mark>95</mark> 分
// 实用标签函数:SQL查询
function sql(strings, ...values) {
return strings.reduce((query, string, i) => {
const value = values[i] !== undefined ? `'${values[i]}'` : "";
return query + string + value;
}, "");
}
const tableName = "users";
const userId = 123;
const query = sql`SELECT * FROM ${tableName} WHERE id = ${userId}`;
console.log(query); // SELECT * FROM 'users' WHERE id = '123'
Symbol 和迭代器
// Symbol - 创建唯一值
const sym1 = Symbol("description");
const sym2 = Symbol("description");
console.log(sym1 === sym2); // false
// 用作对象属性(避免属性名冲突)
const user = {
[Symbol("id")]: 123,
name: "张三",
[Symbol.for("secret")]: "隐藏数据"
};
console.log(Object.keys(user)); // ["name"] - Symbol属性不会被枚举
// 迭代器
const myIterable = {
data: [1, 2, 3, 4, 5],
[Symbol.iterator]: function() {
let index = 0;
const data = this.data;
return {
next: function() {
if (index < data.length) {
return { value: data[index++], done: false };
} else {
return { done: true };
}
}
};
}
};
for (let item of myIterable) {
console.log(item); // 1, 2, 3, 4, 5
}
简单的 Promise
class MyPromise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn());
}
};
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
const handleFulfilled = () => {
try {
const result = onFulfilled(this.value);
resolve(result);
} catch (error) {
reject(error);
}
};
const handleRejected = () => {
if (onRejected) {
try {
const result = onRejected(this.reason);
resolve(result);
} catch (error) {
reject(error);
}
} else {
reject(this.reason);
}
};
if (this.state === 'fulfilled') {
setTimeout(handleFulfilled, 0);
} else if (this.state === 'rejected') {
setTimeout(handleRejected, 0);
} else {
this.onFulfilledCallbacks.push(() => setTimeout(handleFulfilled, 0));
this.onRejectedCallbacks.push(() => setTimeout(handleRejected, 0));
}
});
}
catch(onRejected) {
return this.then(null, onRejected);
}
static resolve(value) {
return new MyPromise(resolve => resolve(value));
}
static reject(reason) {
return new MyPromise((_, reject) => reject(reason));
}
}
// 测试
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => resolve('成功!'), 1000);
});
promise
.then(result => {
console.log('MyPromise结果:', result);
return '继续';
})
.then(result => {
console.log('链式调用:', result);
});
模块化
基本导出和导入
// math.js - 导出模块
// 命名导出
export const PI = 3.14159;
export const E = 2.71828;
export function add(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
}
// 默认导出(每个模块只能有一个)
export default function calculateCircleArea(radius) {
return PI * radius * radius;
}
// 也可以在文件末尾统一导出
const privateVar = "我是私有变量"; // 没有export,外部无法访问
// main.js - 导入模块
// 导入默认导出
import calculateArea from './math.js';
// 导入命名导出
import { PI, add, multiply } from './math.js';
// 同时导入默认导出和命名导出
import calculateArea, { PI, E, multiply } from './math.js';
// 导入所有命名导出作为一个对象
import * as MathUtils from './math.js';
console.log(calculateArea(5)); // 78.53975
console.log(add(2, 3)); // 5
console.log(MathUtils.PI); // 3.14159
console.log(MathUtils.multiply(4, 5)); // 20
导出和导入的变体
// utils.js - 各种导出方式
// 1. 声明时直接导出
export const version = "1.0.0";
// 2. 先声明后导出
const MAX_USERS = 100;
const MIN_AGE = 18;
export { MAX_USERS, MIN_AGE };
// 3. 导出时重命名
const internalName = "utils";
export { internalName as moduleName };
// 4. 重新导出(聚合导出)
export { PI, add } from './math.js';
// 5. 默认导出变量
const defaultConfig = {
apiUrl: "https://api.example.com",
timeout: 5000
};
export default defaultConfig;
// app.js - 各种导入方式
// 导入时重命名
import { multiply as mult } from './math.js';
import { moduleName as utilsName } from './utils.js';
// 同时重命名多个
import {
add as addition,
multiply as multiplication
} from './math.js';
// 导入默认导出并重命名
import { default as config } from './utils.js';
console.log(mult(3, 4)); // 12
console.log(utilsName); // "utils"
动态导入
// 动态导入 - 按需加载模块
async function loadModule(moduleName) {
try {
// import() 返回一个 Promise
const module = await import(`./${moduleName}.js`);
console.log("模块加载成功:", module);
return module;
} catch (error) {
console.error("模块加载失败:", error);
}
}
// 使用动态导入
const button = document.getElementById('loadMathBtn');
button.addEventListener('click', async () => {
const mathModule = await loadModule('math');
if (mathModule) {
const result = mathModule.add(10, 20);
console.log("动态加载模块计算结果:", result);
}
});
// 动态导入的另一种用法
if (userNeedsAdvancedFeatures) {
import('./advanced-features.js')
.then(advancedModule => {
advancedModule.init();
});
}
展开运算符(Spread Operator)
// 数组中的展开运算符
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
// 合并数组
const combined = [...arr1, ...arr2];
console.log(combined); // [1, 2, 3, 4, 5, 6]
// 复制数组(浅拷贝)
const copy = [...arr1];
console.log(copy); // [1, 2, 3]
console.log(copy === arr1); // false - 是新数组
// 在数组中间插入
const withMiddle = [0, ...arr1, 3.5, ...arr2, 7];
console.log(withMiddle); // [0, 1, 2, 3, 3.5, 4, 5, 6, 7]
// 函数调用时展开数组
function sum(a, b, c) {
return a + b + c;
}
const numbers = [1, 2, 3];
console.log(sum(...numbers)); // 6
// 将字符串转换为数组
const str = "hello";
const chars = [...str];
console.log(chars); // ['h', 'e', 'l', 'l', 'o']
// 将类数组对象转换为真正数组
function example() {
const argsArray = [...arguments];
console.log(argsArray); // 真正的数组
}
example(1, 2, 3);
对象的展开运算符
// 对象展开(ES2018)
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
// 合并对象
const merged = { ...obj1, ...obj2 };
console.log(merged); // { a: 1, b: 2, c: 3, d: 4 }
// 复制对象(浅拷贝)
const copy = { ...obj1 };
console.log(copy); // { a: 1, b: 2 }
console.log(copy === obj1); // false
// 对象属性覆盖
const baseConfig = { timeout: 5000, retries: 3 };
const userConfig = { timeout: 10000, apiKey: 'abc123' };
const finalConfig = { ...baseConfig, ...userConfig };
console.log(finalConfig); // { timeout: 10000, retries: 3, apiKey: 'abc123' }
// 添加新属性
const newObj = { ...obj1, e: 5, f: 6 };
console.log(newObj); // { a: 1, b: 2, e: 5, f: 6 }
// 实际应用:状态更新(React/Vue中常用)
const currentState = { user: 'John', posts: [], loading: false };
const updatedState = {
...currentState,
loading: true,
posts: ['Post 1', 'Post 2']
};
console.log(updatedState);
剩余参数(Rest Parameters)
// 函数参数中的剩余参数
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4, 5)); // 15
console.log(sum(10, 20)); // 30
// 剩余参数必须是最后一个参数
function createUser(name, age, ...hobbies) {
return {
name,
age,
hobbies: hobbies.length > 0 ? hobbies : ['暂无爱好']
};
}
const user1 = createUser('张三', 25, '读书', '游泳', '编程');
const user2 = createUser('李四', 30);
console.log(user1);
console.log(user2);
// 与普通参数结合使用
function calculate(operation, ...numbers) {
switch (operation) {
case 'add':
return numbers.reduce((a, b) => a + b, 0);
case 'multiply':
return numbers.reduce((a, b) => a * b, 1);
default:
return 0;
}
}
console.log(calculate('add', 1, 2, 3, 4)); // 10
console.log(calculate('multiply', 2, 3, 4)); // 24
解构赋值中的剩余参数
// 数组解构中的剩余参数
const numbers = [1, 2, 3, 4, 5];
const [first, second, ...rest] = numbers;
console.log(first); // 1
console.log(second); // 2
console.log(rest); // [3, 4, 5]
// 跳过某些元素
const [first, , third, ...others] = numbers;
console.log(first); // 1
console.log(third); // 3
console.log(others); // [4, 5]
// 对象解构中的剩余参数
const person = {
name: '王五',
age: 28,
city: '上海',
country: '中国',
occupation: '工程师'
};
const { name, age, ...details } = person;
console.log(name); // "王五"
console.log(age); // 28
console.log(details); // { city: '上海', country: '中国', occupation: '工程师' }
// 函数参数解构中的剩余参数
function processUser({ id, name, ...preferences }) {
console.log(`处理用户: ${name} (ID: ${id})`);
console.log('用户偏好:', preferences);
}
const user = {
id: 123,
name: '赵六',
theme: 'dark',
language: 'zh-CN',
notifications: true
};
processUser(user);
TypeScript 在 JavaScript 上的新增特性
类型系统
基本类型注解
// JavaScript 中的写法
let name = "张三";
let age = 25;
let isStudent = true;
// TypeScript 类型注解
let name: string = "张三";
let age: number = 25;
let isStudent: boolean = true;
let nothing: null = null;
let notDefined: undefined = undefined;
// 数组类型
let numbers: number[] = [1, 2, 3];
let names: Array<string> = ["张三", "李四"];
// 元组 - 固定长度和类型的数组
let person: [string, number] = ["张三", 25];
// person = [25, "张三"]; // 错误!类型不匹配
任意类型和未知类型
// any - 任意类型(不推荐使用,失去类型检查)
let anything: any = "可以是任意值";
anything = 42;
anything = true;
anything = { name: "张三" };
// unknown - 未知类型(更安全的选择)
let unknownValue: unknown = "hello";
// unknownValue.toUpperCase(); // 错误!需要类型检查
// 使用类型守卫
if (typeof unknownValue === "string") {
console.log(unknownValue.toUpperCase()); // 正确!
}
// void - 没有返回值
function logMessage(message: string): void {
console.log(message);
// 没有 return 语句,或 return undefined
}
// never - 永远不会返回值的函数
function throwError(message: string): never {
throw new Error(message);
}
function infiniteLoop(): never {
while (true) {
// 无限循环
}
}
接口和类型别名
接口定义
// 接口定义对象形状
interface User {
id: number;
name: string;
email: string;
age?: number; // 可选属性
readonly createdAt: Date; // 只读属性
}
// 使用接口
const user: User = {
id: 1,
name: "张三",
email: "zhangsan@example.com",
createdAt: new Date()
};
// user.createdAt = new Date(); // 错误!只读属性不能修改
// 接口扩展
interface Admin extends User {
permissions: string[];
isActive: boolean;
}
const admin: Admin = {
id: 2,
name: "管理员",
email: "admin@example.com",
createdAt: new Date(),
permissions: ["read", "write", "delete"],
isActive: true
};
函数接口
// 函数类型接口
interface MathOperation {
(x: number, y: number): number;
}
const add: MathOperation = (a, b) => a + b;
const multiply: MathOperation = (a, b) => a * b;
console.log(add(5, 3)); // 8
console.log(multiply(4, 6)); // 24
// 可索引接口
interface StringArray {
[index: number]: string;
}
const fruits: StringArray = ["苹果", "香蕉", "橙子"];
console.log(fruits[0]); // "苹果"
类型别名
// 类型别名
type ID = number | string;
type UserRole = "admin" | "user" | "guest";
type Coordinate = [number, number];
// 使用类型别名
const userId: ID = 123;
const userRole: UserRole = "admin";
const location: Coordinate = [40.7128, -74.0060];
// 类型别名 vs 接口
interface Person {
name: string;
age: number;
}
type Employee = Person & { // 交叉类型
employeeId: number;
department: string;
};
const employee: Employee = {
name: "李四",
age: 30,
employeeId: 1001,
department: "技术部"
};
联合类型和交叉类型
联合类型
// 联合类型 - 可以是多种类型之一
type StringOrNumber = string | number;
type Status = "success" | "error" | "loading";
function processInput(input: StringOrNumber): string {
if (typeof input === "string") {
return input.toUpperCase();
} else {
return input.toFixed(2);
}
}
console.log(processInput("hello")); // "HELLO"
console.log(processInput(3.14159)); // "3.14"
// discriminated unions(可辨识联合)
type Shape =
| { kind: "circle"; radius: number }
| { kind: "rectangle"; width: number; height: number }
| { kind: "square"; size: number };
function getArea(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "rectangle":
return shape.width * shape.height;
case "square":
return shape.size ** 2;
}
}
const circle: Shape = { kind: "circle", radius: 5 };
console.log(getArea(circle)); // 78.53981633974483
交叉类型
// 交叉类型 - 合并多个类型
interface Name {
firstName: string;
lastName: string;
}
interface Contact {
email: string;
phone: string;
}
type Person = Name & Contact;
const person: Person = {
firstName: "王",
lastName: "五",
email: "wangwu@example.com",
phone: "13800138000"
};
// 函数重载
function reverse(value: string): string;
function reverse(value: number): number;
function reverse(value: string | number): string | number {
if (typeof value === "string") {
return value.split("").reverse().join("");
} else {
return parseInt(value.toString().split("").reverse().join(""));
}
}
console.log(reverse("hello")); // "olleh"
console.log(reverse(123)); // 321
泛型
基础泛型
// 泛型函数
function identity<T>(arg: T): T {
return arg;
}
const result1 = identity<string>("hello");
const result2 = identity<number>(42);
const result3 = identity("类型推断"); // 自动推断为 string
// 泛型接口
interface Response<T> {
success: boolean;
data: T;
message?: string;
}
const userResponse: Response<User> = {
success: true,
data: {
id: 1,
name: "张三",
email: "zhangsan@example.com",
createdAt: new Date()
}
};
const numberResponse: Response<number[]> = {
success: true,
data: [1, 2, 3, 4, 5]
};
泛型约束
// 泛型约束
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(arg: T): void {
console.log(`长度: ${arg.length}`);
}
logLength("hello"); // 长度: 5
logLength([1, 2, 3]); // 长度: 3
// logLength(42); // 错误!数字没有length属性
// keyof 约束
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { name: "张三", age: 25 };
console.log(getProperty(user, "name")); // "张三"
// console.log(getProperty(user, "email")); // 错误!email不是user的属性
// 泛型类
class Stack<T> {
private items: T[] = [];
push(item: T): void {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
peek(): T | undefined {
return this.items[this.items.length - 1];
}
isEmpty(): boolean {
return this.items.length === 0;
}
}
const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
console.log(numberStack.pop()); // 2
const stringStack = new Stack<string>();
stringStack.push("hello");
stringStack.push("world");
类增强
访问修饰符
class BankAccount {
// 公共属性(默认)
public readonly accountNumber: string;
// 私有属性 - 只能在类内部访问
private balance: number;
// 受保护属性 - 只能在类及其子类中访问
protected owner: string;
constructor(accountNumber: string, owner: string, initialBalance: number = 0) {
this.accountNumber = accountNumber;
this.owner = owner;
this.balance = initialBalance;
}
// 公共方法
public deposit(amount: number): void {
if (amount > 0) {
this.balance += amount;
console.log(`存款成功,当前余额: ${this.balance}`);
}
}
public withdraw(amount: number): boolean {
if (amount > 0 && amount <= this.balance) {
this.balance -= amount;
console.log(`取款成功,当前余额: ${this.balance}`);
return true;
}
console.log("余额不足");
return false;
}
// 获取私有属性的公共方法
public getBalance(): number {
return this.balance;
}
// 私有方法
private logTransaction(type: string, amount: number): void {
console.log(`交易类型: ${type}, 金额: ${amount}, 时间: ${new Date()}`);
}
}
const account = new BankAccount("123456", "张三", 1000);
account.deposit(500);
account.withdraw(200);
// account.balance = 10000; // 错误!balance是私有属性
// account.logTransaction("deposit", 500); // 错误!logTransaction是私有方法
抽象类和静态成员
// 抽象类 - 不能实例化,只能被继承
abstract class Shape {
// 抽象方法 - 必须在子类中实现
abstract getArea(): number;
// 具体方法
getDescription(): string {
return "这是一个形状";
}
// 静态成员
static PI: number = 3.14159;
static calculateCircleArea(radius: number): number {
return this.PI * radius * radius;
}
}
class Circle extends Shape {
constructor(private radius: number) {
super();
}
getArea(): number {
return Math.PI * this.radius ** 2;
}
}
class Rectangle extends Shape {
constructor(private width: number, private height: number) {
super();
}
getArea(): number {
return this.width * this.height;
}
}
// 使用
const circle = new Circle(5);
const rectangle = new Rectangle(4, 6);
console.log(circle.getArea()); // 78.53981633974483
console.log(rectangle.getArea()); // 24
// 访问静态成员
console.log(Shape.PI); // 3.14159
console.log(Shape.calculateCircleArea(3)); // 28.27431
高级类型
映射类型和条件类型
// 映射类型
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Partial<T> = {
[P in keyof T]?: T[P];
};
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
// 使用内置工具类型
interface Product {
id: number;
name: string;
price: number;
description?: string;
}
// 所有属性变为只读
const readonlyProduct: Readonly<Product> = {
id: 1,
name: "手机",
price: 2999
};
// readonlyProduct.name = "电脑"; // 错误!只读属性
// 所有属性变为可选
const partialProduct: Partial<Product> = {
name: "手机"
// 可以只提供部分属性
};
// 选择特定属性
const productName: Pick<Product, "name" | "price"> = {
name: "手机",
price: 2999
};
// 条件类型
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// 提取数组元素类型
type ArrayElement<T> = T extends (infer U)[] ? U : never;
type Numbers = ArrayElement<number[]>; // number
type Strings = ArrayElement<string[]>; // string
模板字面量类型
// 模板字面量类型
type Color = "red" | "green" | "blue";
type Size = "small" | "medium" | "large";
type ButtonVariant = `${Color}-${Size}`;
// 等同于: "red-small" | "red-medium" | "red-large" |
// "green-small" | "green-medium" | "green-large" |
// "blue-small" | "blue-medium" | "blue-large"
const button: ButtonVariant = "red-medium";
// 实用示例:CSS 类名
type Padding = "p-1" | "p-2" | "p-3" | "p-4";
type Margin = "m-1" | "m-2" | "m-3" | "m-4";
type Spacing = `${Padding} ${Margin}`;
const spacing: Spacing = "p-2 m-3";
// 字符串操作类型
type User = {
id: number;
name: string;
email: string;
};
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type UserGetters = Getters<User>;
// 等同于:
// {
// getId: () => number;
// getName: () => string;
// getEmail: () => string;
// }
装饰器
类装饰器和方法装饰器
// 类装饰器
function Logger(constructor: Function) {
console.log(`注册类: ${constructor.name}`);
console.log(`时间: ${new Date().toISOString()}`);
}
function WithTemplate(template: string, hookId: string) {
return function<T extends new (...args: any[]) => object>(originalConstructor: T) {
return class extends originalConstructor {
constructor(...args: any[]) {
super(...args);
const hookEl = document.getElementById(hookId);
if (hookEl) {
hookEl.innerHTML = template;
hookEl.querySelector('h1')!.textContent = (this as any).name;
}
}
};
};
}
@Logger
@WithTemplate('<h1>我的应用</h1>', 'app')
class App {
name: string = "我的TypeScript应用";
constructor() {
console.log("创建应用实例");
}
}
// 方法装饰器
function Log(target: any, propertyName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`调用方法: ${propertyName}`);
console.log(`参数:`, args);
const result = originalMethod.apply(this, args);
console.log(`返回值:`, result);
return result;
};
return descriptor;
}
class Calculator {
@Log
add(a: number, b: number): number {
return a + b;
}
@Log
multiply(a: number, b: number): number {
return a * b;
}
}
const calc = new Calculator();
calc.add(2, 3);
calc.multiply(4, 5);
命名空间和三斜线指令
命名空间
// 命名空间(模块化的一种方式,现代开发推荐使用ES6模块)
namespace MathUtils {
export const PI = 3.14159;
export function calculateCircleArea(radius: number): number {
return PI * radius * radius;
}
export namespace Geometry {
export interface Point {
x: number;
y: number;
}
export function distance(p1: Point, p2: Point): number {
return Math.sqrt((p2.x - p1.x) ** 2 + (p2.y - p1.y) ** 2);
}
}
}
// 使用命名空间
console.log(MathUtils.PI);
console.log(MathUtils.calculateCircleArea(5));
const point1: MathUtils.Geometry.Point = { x: 0, y: 0 };
const point2: MathUtils.Geometry.Point = { x: 3, y: 4 };
console.log(MathUtils.Geometry.distance(point1, point2)); // 5
// 三斜线指令(引用其他文件)
/// <reference path="math-utils.ts" />
配置和编译
tsconfig.json 示例
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020", "DOM"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"removeComments": false,
"noImplicitAny": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"dist",
"**/*.test.ts"
]
}
| 框架/工具 | 作用 |
|---|---|
| Vue2 | |
| Vue Router | 路由 |
| Vuex | 状态管理 |
| Element UI | UI |
| Axios | 请求 |
| Vue I18n | 国际化 |
| 框架/工具 | 作用 |
|---|---|
| Vue3 | |
| Vue Router 4 | 路由 |
| Pinia | 状态管理 |
| Element Plus | UI |
| Axios | 请求 |
| Vue I18n | 国际化 |
| 框架/工具 | 作用 |
|---|---|
| React | |
| React Router v6 | 路由 |
| MobX | 状态管理 |
| Ant Design | UI |
| Axios | 请求 |
| react-i18next | 国际化 |
| 框架/工具 | 作用 |
|---|---|
| UniApp | 小程序 / 移动端开发 |
| Taro | 小程序 / 移动端开发 |
| NextJS | Web开发 |
© 版权声明
文章版权归作者所有,未经允许请勿转载。
相关文章
暂无评论...