js封装系列(一)
适用场景
封装 sdk 场景下,在 sdk 中对公共对象以及方法进行 proxy 操作。
团队 A 有 A-sdk 对一个全局对象进行了改写 proxy 操作。此时,团队 B 开发了 B-sdk 对相同的全局对象的方法也进行了对应的改写。
出现问题
在一个项目中同时使用了 A-sdk 和 B-sdk,该项目研发同学在测试时发现 A 团队提供的 A-sdk 的对应功能失效了。
问题复现
创建原型
创建一个 base 工厂函数并配置对应的原型链方法 x
js
// 原型
function base(){};
// 创建原型链方法
base.prototype.x = function(){
return "base";
}
// 实例化
const baseEg = new base();
第一次劫持
模拟 A-sdk 劫持 x 方法,并添加一些业务逻辑
js
const getX = baseEg.x;
baseEg.x = function(){
return `proxy-${getX()}`;
}
baseEg.x(); // 输出 "proxy-base" 证明劫持成功
第二次劫持
模拟 B-sdk 劫持 x 方法,并添加 B 团队的业务逻辑
方案1
js
// func1 实现
const func1GetX = baseEg.x;
baseEg.x = function() {
return `func1-${func1GetX()}`;
}
baseEg.x();
// 输出 "func1-proxy-base"
方案2:
js
// func2 实现
const func2GetX = Object.getPrototypeOf(baseEg).x;
baseEg.x = function() {
return `func2-${func2GetX()}`;
}
baseEg.x(); // 输出 "func2-base"
综合对比
方案1 | 方案2 | |
---|---|---|
思路 | 基于当前对象上的某个方法进行封装 | 基于当前对象原型链上的某个方法进行封装 |
输出 | "func1-proxy-base" | "func2-base" |
结论 | 不会破坏其他库对该方法的封装 | 基于原型链方法进行封装会破坏其他库对 |
至此问题已浮出水面。
警示
在封装sdk过程中:
- 尽量 不修改全局对象(遵循如无必要勿增实体的原则);
- 不要覆盖存在于原型链上的方法(上面方案2的做法);
- 提醒用户检查其他sdk是不是有类似的操作(上面方案2的做法);