是否可实现不受猴子补丁破坏的稳健Function.prototype.bind polyfill?
是存在可实现的方案的,核心思路是在所有用户代码执行前预先缓存所有要用到的原生方法私有副本,所有polyfill逻辑完全依赖这些不对外暴露的私有副本,不访问全局对象或原型链上的公开方法,具体实现逻辑如下:
核心实现步骤
- 必须将polyfill的初始化代码放在整个执行上下文的最顶部,保证此时
Function.prototype.apply、Array.prototype.slice、Object.defineProperty等依赖的原生方法还没有被用户篡改 - 用闭包私有变量缓存这些原生方法的副本,永远不要将这些副本暴露到全局作用域,避免被后续的猴子补丁修改
- polyfill返回的绑定函数完全依赖闭包内的私有原生方法实现逻辑,不要调用任何公开原型链上的方法
示例实现
// 初始化代码必须在所有业务代码之前运行 (function () { // 缓存所有需要的原生方法私有副本 const privateApply = Function.prototype.apply; const privateSlice = Array.prototype.slice; const privateDefineProperty = Object.defineProperty; // 仅当原生bind不存在时才注入polyfill if (typeof Function.prototype.bind !== 'function') { privateDefineProperty(Function.prototype, 'bind', { value: function (thisArg /* , 预设参数 */) { if (typeof this !== 'function') { throw new TypeError('Function.prototype.bind 只能被函数调用'); } const targetFn = this; const boundArgs = privateSlice.call(arguments, 1); // 返回的绑定函数完全依赖闭包内的私有方法 function boundFn(/* 调用参数 */) { const callArgs = privateSlice.call(arguments, 0); const finalArgs = boundArgs.concat(callArgs); // 处理new调用的场景,符合ES规范 if (this instanceof boundFn) { return privateApply(targetFn, this, finalArgs); } return privateApply(targetFn, thisArg, finalArgs); } // 对齐原生bind返回函数的原型、length等属性 boundFn.prototype = targetFn.prototype; privateDefineProperty(boundFn, 'length', { value: Math.max(targetFn.length - boundArgs.length, 0), writable: false, enumerable: false, configurable: true }); return boundFn; }, writable: true, configurable: true, enumerable: false }); } })();
方案限制
该方案生效的前提是你拥有脚本执行顺序的控制权,能保证polyfill代码在所有用户代码之前运行。如果运行polyfill时,依赖的原生方法已经被用户篡改,就无法保证返回的绑定函数不受猴子补丁破坏。
内容的提问来源于stack exchange,提问作者Melab
相关产品推荐
相关产品推荐

