【前端】Polyfill 和 Transpiler (Babel)

发布于:2025-06-28 ⋅ 阅读:(16) ⋅ 点赞:(0)

Polyfill 垫片/兼容性补丁

  • 定义:Polyfill 是一段 JavaScript 代码,它用于在旧的浏览器或 JavaScript 环境中,提供现代浏览器或最新 JavaScript 规范中才有的功能。
  • 应用:
  1. Promise Polyfill: Promise 是 ES6 (ECMAScript 2015) 引入的异步编程解决方案。在 IE 浏览器等不支持 Promise 的旧环境中,Promise Polyfill 就会用 ES5 的语法模拟 Promise 的行为和 API,让 new Promise(…)、.then()、.catch() 都能正常工作。
  2. Array.prototype.includes Polyfill: includes 方法可以判断数组是否包含某个元素。在一些旧浏览器中,这个方法可能不存在。Polyfill 就会为 Array.prototype 添加一个 includes 方法,其内部实现可能就是用 indexOf 或者一个简单的 for 循环来模拟。
  • Polyfill 和 Transpiler (Babel)区别
    • Transpiler其实是一种语法转换器,新转成旧的。
      Transpiler (Babel): 将 ES6+ 的新语法(如箭头函数 =>、const/let、类 class)转换成 ES5 语法,让旧浏览器能理解。它不提供新的 API 或功能。 const a = 1; 转换成 var a = 1;
    • Polyfill: 填充新功能/API。它在运行时检测并提供旧环境缺少的新的全局对象、方法或属性。
      例子: 在 IE 浏览器中添加 Promise 全局对象,或为 Array.prototype 添加 includes 方法。

Promise 的 Polyfill 源码

  • 核心点
  1. 状态机 (States):
    Promise 必须有三种状态:pending (进行中), fulfilled (已成功), rejected (已失败)。
    状态只能从 pending 转换为 fulfilled 或 rejected,并且一旦改变就不可逆。
    源码中会用数字或字符串常量来表示这些状态,并有逻辑来严格控制状态转换。
  2. then 方法:
    这是 Promise 最核心的链式调用方法,它接收两个可选参数:onFulfilled(成功回调)和 onRejected(失败回调)。
    无论 Promise 状态是否已经完成(是 pending、fulfilled 还是 rejected),只要后续一直链式调用 .then(),它就会返回一个新的 Promise 实例,并且会执行传入的回调函数。
    回调的异步执行: onFulfilled 和 onRejected 回调必须在当前事件循环的**微任务(Microtask)**中异步执行,以符合 Promise A+ 规范。源码中会通过 queueMicrotask、process.nextTick (Node.js) 或模拟 MutationObserver/setTimeout(0) 来实现。
    值的传递与穿透: 理解成功或失败的值是如何从一个 Promise 传递到下一个 then 的回调中,以及当回调返回一个 Promise 时如何进行展平(flattening)。
  3. resolve 和 reject 函数:
    这是 Promise 构造函数中接收的两个函数,用于改变 Promise 的状态。
    resolve 函数需要处理接收到的值,特别是当值本身是一个 Promise 时,需要进行递归解析(即展平)。
  4. 异常处理:
    理解 catch 方法的本质(它是 then(null, onRejected) 的语法糖)。
    理解未捕获的拒绝(unhandled rejections)是如何处理的。
    .catch() 方法确实只有在 Promise 链中发生错误(即 Promise 的状态变为 rejected)时才会进入并执行。进入.catch()之后如果有.then(),除非是“.catch()抛出新错误且.then()中没有onRejected”时才不会执行,否则其他情况都会执行
/**
 * 模拟 Promises/A+ 规范的 Promise Polyfill
 */
class MyPromise {
    // 定义 Promise 的三种状态
    static PENDING = 'pending';
    static FULFILLED = 'fulfilled';
    static REJECTED = 'rejected';

    constructor(executor) {
    //executor执行器函数,如:网络请求 fetch...
        // 初始化 Promise 状态
        this.status = MyPromise.PENDING;
        // 存储 Promise 成功时的值
        this.value = undefined;
        // 存储 Promise 失败时的原因
        this.reason = undefined;
        // 存储成功回调函数队列 (因为可能then多次)
        this.onFulfilledCallbacks = [];
        // 存储失败回调函数队列 (因为可能then多次)
        this.onRejectedCallbacks = [];

        // 绑定 resolve 方法的 this,确保其上下文正确指向 MyPromise 实例
        const resolve = (value) => {
            // Promise 状态只能从 pending 变为 fulfilled
            if (this.status === MyPromise.PENDING) {
                this.status = MyPromise.FULFILLED;
                this.value = value;
                // 异步执行所有成功的 onFulfilled 回调
                this.onFulfilledCallbacks.forEach(callback => {
                    // 使用 setTimeout 模拟微任务,确保异步性 (实际生产环境会用更优方案)
                    setTimeout(() => callback(this.value), 0);
                });
            }
        };

        // 绑定 reject 方法的 this,确保其上下文正确指向 MyPromise 实例
        const reject = (reason) => {
            // Promise 状态只能从 pending 变为 rejected
            if (this.status === MyPromise.PENDING) {
                this.status = MyPromise.REJECTED;
                this.reason = reason;
                // 异步执行所有失败的 onRejected 回调
                this.onRejectedCallbacks.forEach(callback => {
                    // 使用 setTimeout 模拟微任务
                    setTimeout(() => callback(this.reason), 0);
                });
            }
        };

        try {
            // 立即执行 executor 函数,并传入 resolve 和 reject 作为参数
            // executor 可能会同步或异步地调用 resolve 或 reject
            executor(resolve, reject);
        } catch (error) {
            // 如果 executor 执行过程中抛出异常,则直接调用 reject
            reject(error);
        }
    }

    /**
     * then 方法用于注册成功和失败的回调
     * @param {Function} onFulfilled Promise 成功时调用的回调
     * @param {Function} onRejected Promise 失败时调用的回调
     * @returns {MyPromise} 返回一个新的 Promise 实例,支持链式调用
     */
    then(onFulfilled, onRejected) {
        // 为 onFulfilled 和 onRejected 设置默认值,确保它们是函数
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
        onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };

        // 必须返回一个新的 Promise 实例
        const newPromise = new MyPromise((resolve, reject) => {
            // 处理 Promise 处于 PENDING 状态的情况:将回调添加到队列
            if (this.status === MyPromise.PENDING) {
                this.onFulfilledCallbacks.push(() => {
                    // 确保回调在微任务中执行,并处理返回值 x
                    setTimeout(() => {
                        try {
                            const x = onFulfilled(this.value);
                            // 调用 resolvePromise 来处理 x 的值,实现 Promise 展平
                            resolvePromise(newPromise, x, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    }, 0);
                });

                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            const x = onRejected(this.reason);
                            resolvePromise(newPromise, x, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    }, 0);
                });
            }
            // 处理 Promise 已经处于 FULFILLED 状态的情况:立即异步执行回调
            else if (this.status === MyPromise.FULFILLED) {
                setTimeout(() => {
                    try {
                        const x = onFulfilled(this.value);
                        resolvePromise(newPromise, x, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                }, 0);
            }
            // 处理 Promise 已经处于 REJECTED 状态的情况:立即异步执行回调
            else if (this.status === MyPromise.REJECTED) {
                setTimeout(() => {
                    try {
                        const x = onRejected(this.reason);
                        resolvePromise(newPromise, x, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                }, 0);
            }
        });

        return newPromise;
    }

    // Sugar for then(null, onRejected)
    catch(onRejected) {
        return this.then(null, onRejected);
    }

    // Static method for Promise.resolve()
    static resolve(value) {
        return new MyPromise(resolve => resolve(value));
    }

    // Static method for Promise.reject()
    static reject(reason) {
        return new MyPromise((_, reject) => reject(reason));
    }
}

/**
 * 这是一个辅助函数,用于处理 then 方法返回的回调 x 的值。
 * 它是 Promises/A+ 规范中非常关键的一部分,用于实现 Promise 的展平。
 * 如:异步操作的结果又是另一个异步操作。
 * @param {MyPromise} promise 正在被解决的 promise (then 方法返回的那个新 promise)
 * @param {*} x onFulfilled 或 onRejected 回调的返回值
 * @param {Function} resolve newPromise 的 resolve 函数
 * @param {Function} reject newPromise 的 reject 函数
 */
function resolvePromise(promise, x, resolve, reject) {
    // 规范 2.3.1: 如果 promise 和 x 指向同一个对象,则以 TypeError 为原因拒绝 promise
    if (promise === x) {
        return reject(new TypeError('Chaining cycle detected for promise #<MyPromise>'));
    }

    // 规范 2.3.2: 如果 x 是一个 Promise
    if (x instanceof MyPromise) {
        // 展平 Promise:x 的状态决定 promise 的状态
        x.then(value => {
            // 如果 x 被 resolve,则递归调用 resolvePromise 再次处理 value
            resolvePromise(promise, value, resolve, reject);
        }, reason => {
            // 如果 x 被 reject,则直接 reject promise
            reject(reason);
        });
        return;
    }

    // 规范 2.3.3: 如果 x 是一个对象或函数 (thenable)
    if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
        let called = false; // 用于防止多次调用 resolve/reject

        try {
            // 尝试获取 x 的 then 方法
            const then = x.then;

            if (typeof then === 'function') {
                // 如果 then 是一个函数,则调用它,以 x 为 this,并传入 resolvePromise 和 reject 作为参数
                then.call(x, y => {
                    if (called) return;
                    called = true;
                    // 递归处理 y
                    resolvePromise(promise, y, resolve, reject);
                }, r => {
                    if (called) return;
                    called = true;
                    reject(r);
                });
            } else {
                // 如果 then 不是函数,则直接 resolve promise
                resolve(x);
            }
        } catch (error) {
            // 如果获取 x.then 或调用 then 过程中发生错误
            if (called) return; // 防止在 try-catch 块中已经调用过
            called = true;
            reject(error);
        }
        return;
    }

    // 规范 2.3.4: 如果 x 不属于以上任何情况,则直接以 x 为值解决 promise
    resolve(x);
}

// 模拟全局环境,将 MyPromise 挂载到全局
// if (typeof window !== 'undefined' && !window.Promise) {
//     window.Promise = MyPromise;
// } else if (typeof global !== 'undefined' && !global.Promise) {
//     global.Promise = MyPromise;
// }

//使用
// 假设你已经定义了上面的 MyPromise 类

function asyncOperation(shouldSucceed) {
    return new MyPromise((resolve, reject) => {
        setTimeout(() => {
            if (shouldSucceed) {
                resolve('操作成功!');
            } else {
                reject(new Error('操作失败!'));
            }
        }, 1000);
    });
}

// 示例 1: 成功链式调用
asyncOperation(true)
    .then(result => {
        console.log('第一个 then 收到:', result); // "第一个 then 收到: 操作成功!"
        return result + ' - 加工';
    })
    .then(processedResult => {
        console.log('第二个 then 收到:', processedResult); // "第二个 then 收到: 操作成功! - 加工"
        return new MyPromise(res => setTimeout(() => res('这是内部 Promise'), 500));
    })
    .then(innerResult => {
        console.log('展平后的结果:', innerResult); // "展平后的结果: 这是内部 Promise"
    })
    .catch(error => {
        console.error('捕获到错误:', error.message);
    });

// 示例 2: 失败处理
asyncOperation(false)
    .then(result => {
        console.log('不应该成功:', result);
    })
    .catch(error => {
        console.error('捕获到错误:', error.message); // "捕获到错误: 操作失败!"
        return '错误已被处理,返回新值'; // 可以在 catch 中返回一个值,使链式继续成功
    })
    .then(finalResult => {
        console.log('错误处理后的下一个 then:', finalResult); // "错误处理后的下一个 then: 错误已被处理,返回新值"
    });

// 示例 3: 同步错误
new MyPromise((resolve, reject) => {
    throw new Error('同步抛出的错误');
})
.catch(error => {
    console.error('捕获到同步错误:', error.message); // "捕获到同步错误: 同步抛出的错误"
});

网站公告

今日签到

点亮在社区的每一天
去签到