JS 语法学习路线

基本语法

变量声明


// 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开发
© 版权声明

相关文章

暂无评论

none
暂无评论...