跳转至

01 - JavaScript深度理解

目标: 深入理解JavaScript核心机制,能手写Promise等核心API

时间: 2周

核心原则: 理解原理比会用更重要


🎯 为什么需要深入理解JavaScript?

实际场景

Text Only
场景1: 异步代码执行顺序混乱
- 不了解事件循环 → 无法预测代码执行顺序
- 不了解微任务宏任务 → 出现诡异bug

场景2: 内存泄漏
- 不了解闭包原理 → 造成内存泄漏
- 不了解垃圾回收 → 无法优化内存使用

场景3: 框架原理理解困难
- 不了解原型链 → 难以理解Vue/React的继承机制
- 不了解ES Module → 难以理解构建工具

📚 核心知识点

1. 执行上下文与作用域

JavaScript
// 执行上下文类型
// 1. 全局执行上下文
// 2. 函数执行上下文
// 3. Eval执行上下文

// 作用域链:内部函数可沿作用域链向上查找外部变量
function outer() {
    const a = 1;               // outer的局部变量
    function inner() {
        const b = 2;           // inner的局部变量
        console.log(a);        // 沿作用域链查找,找到outer中的a → 输出1
    }
    inner();
}

// 变量提升(Hoisting):var声明会被提升到作用域顶部,但赋值不会
console.log(a); // undefined(声明被提升,但此时还未赋值)
var a = 1;

// let/const 暂时性死区(TDZ):在声明前访问会报错,比var更安全
console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 2;

2. 闭包(Closure)

JavaScript
// 闭包的本质:函数+词法环境(函数能记住并访问其定义时的作用域)
function createCounter() {
    let count = 0;  // 自由变量:被内部函数引用,不会被垃圾回收

    // 返回的对象中的方法都共享同一个count变量(形成闭包)
    return {
        increment: function() {
            count++;            // 通过闭包访问并修改count
            return count;
        },
        decrement: function() {
            count--;
            return count;
        },
        getCount: function() {
            return count;       // 通过闭包读取count的当前值
        }
    };
}

const counter = createCounter();    // 创建一个独立的计数器实例
console.log(counter.increment());   // 1(count从0变为1)
console.log(counter.increment());   // 2(count从1变为2)
console.log(counter.getCount());    // 2(读取当前count值)
// 注意:外部无法直接访问count变量,实现了数据私有化

// 实际应用:模块化模式(IIFE + 闭包实现私有成员)
const Module = (function() {
    // 私有变量和方法:外部无法直接访问
    let privateVar = 0;

    function privateMethod() {
        console.log('Private method');
    }

    // 只暴露公共接口,隐藏内部实现细节
    return {
        publicMethod: function() {
            privateVar++;           // 通过闭包访问私有变量
            privateMethod();        // 通过闭包调用私有方法
            console.log('Public method', privateVar);
        }
    };
})();  // 立即执行函数表达式(IIFE),执行后返回公共接口对象

Module.publicMethod(); // Private method \n Public method 1

3. 原型链与继承

JavaScript
// 原型链的本质:每个对象都有一个内部链接指向其原型对象,形成链式结构
function Person(name) {
    this.name = name;       // 实例属性:每个实例独有一份
}

// 原型方法:所有实例共享同一份,节省内存
Person.prototype.sayHello = function() {
    console.log(`Hello, I'm ${this.name}`);
};

const alice = new Person('Alice');
alice.sayHello(); // Hello, I'm Alice(先在实例上找,找不到则沿原型链向上查找)

// 原型链查找路径:alice → Person.prototype → Object.prototype → null
console.log(alice.__proto__ === Person.prototype);              // true(实例的原型指向构造函数的prototype)
console.log(Person.prototype.__proto__ === Object.prototype);   // true(所有对象最终继承自Object)
console.log(Object.prototype.__proto__);                        // null(原型链的终点)

// 手写new操作符——理解new关键字背后的三步操作
function myNew(constructor, ...args) {  // ...展开运算符:展开数组/对象
    // 第1步:创建空对象,并将其原型指向构造函数的prototype(建立原型链)
    const obj = Object.create(constructor.prototype);
    // 第2步:执行构造函数,将this绑定到新对象(初始化实例属性)
    const result = constructor.apply(obj, args);
    // 第3步:如果构造函数显式返回了对象则使用该对象,否则返回新创建的对象
    return result instanceof Object ? result : obj;
}

// ES6 Class语法糖——底层仍然基于原型链,但写法更清晰
class Animal {
    constructor(name) {
        this.name = name;   // 构造函数中初始化实例属性
    }

    speak() {               // 定义在prototype上的方法
        console.log(`${this.name} makes a sound`);
    }
}

// extends实现继承:Dog.prototype.__proto__ === Animal.prototype
class Dog extends Animal {
    speak() {               // 方法重写(override):子类覆盖父类的同名方法
        console.log(`${this.name} barks`);
    }
}

const dog = new Dog('Buddy');
dog.speak(); // Buddy barks(调用Dog自身的speak,不会沿原型链找到Animal的)

4. 事件循环(Event Loop)

JavaScript
// === 事件循环机制(Event Loop) ===
// 执行顺序:同步代码 → 微任务队列(Promise.then等)→ 宏任务队列(setTimeout等)

console.log('1');                   // 【同步】立即执行,进入调用栈

setTimeout(() => {
    console.log('2');               // 【宏任务】放入宏任务队列,等待下一轮事件循环
}, 0);

Promise.resolve().then(() => {
    console.log('3');               // 【微任务】放入微任务队列,本轮同步代码执行完后立即执行
});

console.log('4');                   // 【同步】立即执行

// 输出顺序:1, 4, 3, 2
// 解析:同步(1,4) → 微任务(3) → 宏任务(2)

// === 更复杂的例子:嵌套的宏任务和微任务 ===
console.log('start');               // 【同步】

setTimeout(() => {                  // 【宏任务1】进入宏任务队列
    console.log('setTimeout1');
    Promise.resolve().then(() => {  // 宏任务1执行时产生的微任务,在该宏任务结束前执行
        console.log('promise1');
    });
}, 0);

setTimeout(() => {                  // 【宏任务2】进入宏任务队列(排在宏任务1后面)
    console.log('setTimeout2');
}, 0);

Promise.resolve().then(() => {      // 【微任务1】进入微任务队列
    console.log('promise2');
    setTimeout(() => {              // 微任务1执行时产生的宏任务,进入宏任务队列
        console.log('setTimeout3');
    }, 0);
});

console.log('end');                 // 【同步】

// 输出:start, end, promise2, setTimeout1, promise1, setTimeout2, setTimeout3
// 解析:同步(start,end) → 微任务(promise2) → 宏任务1(setTimeout1) → 其微任务(promise1) → 宏任务2(setTimeout2) → 宏任务3(setTimeout3)

5. 手写Promise

JavaScript
// ========== 手写Promise:完整实现Promises/A+规范 ==========
class MyPromise {
    constructor(executor) {
        this.state = 'pending';             // Promise三种状态:pending(等待)、fulfilled(成功)、rejected(失败)
        this.value = undefined;             // 成功时的值
        this.reason = undefined;            // 失败时的原因
        this.onFulfilledCallbacks = [];     // 成功回调队列(支持异步场景下多次then调用)
        this.onRejectedCallbacks = [];      // 失败回调队列

        // resolve函数:将状态从pending变为fulfilled
        const resolve = (value) => {  // 箭头函数:简洁的函数语法
            if (this.state === 'pending') { // 状态只能从pending转变,且只能转变一次
                this.state = 'fulfilled';
                this.value = value;
                // 依次执行所有等待中的成功回调(异步resolve场景)
                this.onFulfilledCallbacks.forEach(fn => fn());
            }
        };

        // reject函数:将状态从pending变为rejected
        const reject = (reason) => {  // const不可重新赋值;let块级作用域变量
            if (this.state === 'pending') {
                this.state = 'rejected';
                this.reason = reason;
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        };

        // 立即执行executor,捕获同步异常自动reject
        try {  // try/catch捕获异常
            executor(resolve, reject);
        } catch (error) {
            reject(error);
        }
    }

    // then方法:注册成功和失败的回调,返回新的Promise(实现链式调用)
    then(onFulfilled, onRejected) {
        // 参数默认值处理:实现值穿透(.then().then(v => ...)仍能拿到值)
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
        onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };

        // 返回新的Promise,实现链式调用的核心
        const promise2 = new MyPromise((resolve, reject) => {
            if (this.state === 'fulfilled') {
                // 用setTimeout模拟微任务,确保异步执行(规范要求)
                setTimeout(() => {
                    try {
                        const x = onFulfilled(this.value);  // 执行成功回调
                        resolvePromise(promise2, x, resolve, reject);  // 处理返回值
                    } catch (error) {
                        reject(error);  // 回调中抛出的异常传递给下一个Promise
                    }
                }, 0);
            } else if (this.state === 'rejected') {
                setTimeout(() => {
                    try {
                        const x = onRejected(this.reason);  // 执行失败回调
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                }, 0);
            } else {
                // pending状态:将回调存入队列,等待resolve/reject时执行
                this.onFulfilledCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            const x = onFulfilled(this.value);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    }, 0);
                });

                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            const x = onRejected(this.reason);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    }, 0);
                });
            }
        });

        return promise2;    // 返回新Promise,支持 .then().then() 链式调用
    }

    // catch方法:语法糖,等价于 .then(null, onRejected)
    catch(onRejected) {
        return this.then(null, onRejected);
    }

    // 静态方法:快速创建已成功的Promise
    static resolve(value) {
        return new MyPromise((resolve) => resolve(value));
    }

    // 静态方法:快速创建已失败的Promise
    static reject(reason) {
        return new MyPromise((_, reject) => reject(reason));
    }

    // Promise.all:所有Promise都成功才成功,任一失败则立即失败
    static all(promises) {
        return new MyPromise((resolve, reject) => {
            const results = [];     // 存储每个Promise的结果
            let count = 0;          // 已完成的Promise计数

            promises.forEach((promise, index) => {
                Promise.resolve(promise).then(value => {  // Promise异步操作容器:pending→fulfilled/rejected
                    results[index] = value;     // 按原始顺序存放结果(不是按完成顺序)
                    count++;
                    if (count === promises.length) {
                        resolve(results);       // 全部完成,resolve总结果
                    }
                }).catch(reject);               // 任一失败,直接reject
            });
        });
    }
}

// Promise解析过程(Resolution Procedure):处理then回调的返回值
// 这是Promises/A+规范的核心,决定了链式调用的行为
function resolvePromise(promise2, x, resolve, reject) {
    // 防止循环引用:then回调返回了自身Promise
    if (promise2 === x) {
        reject(new TypeError('Chaining cycle detected'));
        return;
    }

    // 如果x是对象或函数,尝试将其作为thenable处理
    if (x && (typeof x === 'object' || typeof x === 'function')) {
        let called = false;     // 防止resolve和reject被多次调用
        try {
            const then = x.then;
            if (typeof then === 'function') {
                // x是thenable(含then方法的对象/函数),递归解析
                then.call(x, y => {
                    if (called) return;     // 确保只调用一次
                    called = true;
                    resolvePromise(promise2, y, resolve, reject);  // 递归处理(y可能还是Promise)
                }, r => {
                    if (called) return;
                    called = true;
                    reject(r);
                });
            } else {
                resolve(x);    // x有then属性但不是函数,直接resolve
            }
        } catch (error) {
            if (called) return;
            called = true;
            reject(error);
        }
    } else {
        resolve(x);            // x是普通值(非对象非函数),直接resolve
    }
}

// ========== 测试 ==========
const p = new MyPromise((resolve, reject) => {
    setTimeout(() => resolve('Success!'), 1000);    // 1秒后异步resolve
});

// 链式调用:每个then返回新的Promise
p.then(value => {
    console.log(value);         // 输出:Success!
    return 'Next value';        // 返回值会传递给下一个then
}).then(value => {
    console.log(value);         // 输出:Next value
});

✅ 学习检查点

  • 理解执行上下文和作用域链
  • 能解释闭包的原理和应用
  • 理解原型链和继承机制
  • 能解释事件循环机制
  • 能手写Promise实现

记住:JavaScript是前端的基础,深入理解它才能掌握框架原理! 📚