Polyfill 垫片/兼容性补丁
定义:Polyfill 是一段 JavaScript 代码,它用于在旧的浏览器或 JavaScript 环境中,提供现代浏览器或最新 JavaScript 规范中才有的功能。
应用:
Promise Polyfill: Promise 是 ES6 (ECMAScript 2015) 引入的异步编程解决方案。在 IE 浏览器等不支持 Promise 的旧环境中,Promise Polyfill 就会用 ES5 的语法模拟 Promise 的行为和 API,让 new Promise(…)、.then()、.catch() 都能正常工作。
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 源码
状态机 (States): Promise 必须有三种状态:pending (进行中), fulfilled (已成功), rejected (已失败)。 状态只能从 pending 转换为 fulfilled 或 rejected,并且一旦改变就不可逆。 源码中会用数字或字符串常量来表示这些状态,并有逻辑来严格控制状态转换。
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)。
resolve 和 reject 函数: 这是 Promise 构造函数中接收的两个函数,用于改变 Promise 的状态。 resolve 函数需要处理接收到的值,特别是当值本身是一个 Promise 时,需要进行递归解析(即展平)。
异常处理: 理解 catch 方法的本质(它是 then(null, onRejected) 的语法糖)。 理解未捕获的拒绝(unhandled rejections)是如何处理的。 .catch() 方法确实只有在 Promise 链中发生错误(即 Promise 的状态变为 rejected)时才会进入并执行。进入.catch()之后如果有.then(),除非是“.catch()抛出新错误且.then()中没有onRejected”时才不会执行,否则其他情况都会执行
class MyPromise {
static PENDING = 'pending' ;
static FULFILLED = 'fulfilled' ;
static REJECTED = 'rejected' ;
constructor ( executor ) {
this . status = MyPromise. PENDING ;
this . value = undefined ;
this . reason = undefined ;
this . onFulfilledCallbacks = [ ] ;
this . onRejectedCallbacks = [ ] ;
const resolve = ( value ) => {
if ( this . status === MyPromise. PENDING ) {
this . status = MyPromise. FULFILLED ;
this . value = value;
this . onFulfilledCallbacks. forEach ( callback => {
setTimeout ( ( ) => callback ( this . value) , 0 ) ;
} ) ;
}
} ;
const reject = ( reason ) => {
if ( this . status === MyPromise. PENDING ) {
this . status = MyPromise. REJECTED ;
this . reason = reason;
this . onRejectedCallbacks. forEach ( callback => {
setTimeout ( ( ) => callback ( this . reason) , 0 ) ;
} ) ;
}
} ;
try {
executor ( resolve, reject) ;
} catch ( error) {
reject ( error) ;
}
}
then ( onFulfilled, onRejected ) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; } ;
const newPromise = new MyPromise ( ( resolve, reject ) => {
if ( this . status === MyPromise. PENDING ) {
this . onFulfilledCallbacks. push ( ( ) => {
setTimeout ( ( ) => {
try {
const x = onFulfilled ( this . value) ;
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 ) ;
} ) ;
}
else if ( this . status === MyPromise. FULFILLED ) {
setTimeout ( ( ) => {
try {
const x = onFulfilled ( this . value) ;
resolvePromise ( newPromise, x, resolve, reject) ;
} catch ( error) {
reject ( error) ;
}
} , 0 ) ;
}
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;
}
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) ) ;
}
}
function resolvePromise ( promise, x, resolve, reject ) {
if ( promise === x) {
return reject ( new TypeError ( 'Chaining cycle detected for promise #<MyPromise>' ) ) ;
}
if ( x instanceof MyPromise ) {
x. then ( value => {
resolvePromise ( promise, value, resolve, reject) ;
} , reason => {
reject ( reason) ;
} ) ;
return ;
}
if ( ( typeof x === 'object' && x !== null ) || typeof x === 'function' ) {
let called = false ;
try {
const then = x. then;
if ( typeof then === 'function' ) {
then . call ( x, y => {
if ( called) return ;
called = true ;
resolvePromise ( promise, y, resolve, reject) ;
} , r => {
if ( called) return ;
called = true ;
reject ( r) ;
} ) ;
} else {
resolve ( x) ;
}
} catch ( error) {
if ( called) return ;
called = true ;
reject ( error) ;
}
return ;
}
resolve ( x) ;
}
function asyncOperation ( shouldSucceed ) {
return new MyPromise ( ( resolve, reject ) => {
setTimeout ( ( ) => {
if ( shouldSucceed) {
resolve ( '操作成功!' ) ;
} else {
reject ( new Error ( '操作失败!' ) ) ;
}
} , 1000 ) ;
} ) ;
}
asyncOperation ( true )
. then ( result => {
console. log ( '第一个 then 收到:' , result) ;
return result + ' - 加工' ;
} )
. then ( processedResult => {
console. log ( '第二个 then 收到:' , processedResult) ;
return new MyPromise ( res => setTimeout ( ( ) => res ( '这是内部 Promise' ) , 500 ) ) ;
} )
. then ( innerResult => {
console. log ( '展平后的结果:' , innerResult) ;
} )
. catch ( error => {
console. error ( '捕获到错误:' , error. message) ;
} ) ;
asyncOperation ( false )
. then ( result => {
console. log ( '不应该成功:' , result) ;
} )
. catch ( error => {
console. error ( '捕获到错误:' , error. message) ;
return '错误已被处理,返回新值' ;
} )
. then ( finalResult => {
console. log ( '错误处理后的下一个 then:' , finalResult) ;
} ) ;
new MyPromise ( ( resolve, reject ) => {
throw new Error ( '同步抛出的错误' ) ;
} )
. catch ( error => {
console. error ( '捕获到同步错误:' , error. message) ;
} ) ;