如何在不改变原代码的情况下 修改obj对象
const x = (() => {
const obj = {
a: 1,
b: 2,
};
return {
get: y => obj[y]
};
})();
代码分析:
首先分析这几行代码究竟是什么意思又包含了哪些东西?
- 一个IIFE立即执行函数
- 这个立即执行函数里包含了一个obj对象
- 该函数又返回了另一个对象
- 该对象有一个get方法,传入一个参数可以获取到obj的属性
console.log(x.get('a')); // 1
console.log(x.get('b')); // 2
这是一个典型的闭包场景,目的是为了避免obj受到全局污染,保护函数的内部变量不受外部的干扰,形成一个不销毁的内存,从而保护了对象的完整。
解决思路:
是否可以通过它本身所提供的API达到修改目的?
通过get方法可以访问到obj的全部属性,除了它的自有属性,还有它所继承的原型对象上的属性和方法。
obj对象的原型 -> Object.prototype
是否可以通过调用原型对象上的valueOf方法来获取到obj对象本身?
console.log(x.get('valueOf')); // [Function: valueOf]
valueOf方法会返回指定对象的原始值,若该对象没有原始值,则返回自身,比如:
let user = {
userName: '条条',
age: 23
};
console.log(user.valueOf()); // { userName: '条条', age: 23 }
console.log(user.valueOf() === user); // true
所以obj调用valueOf的话,返回的也应该是obj这个对象本身。
console.log(x.get('valueOf')());
// TypeError: Cannot convert undefined or null to object
然而调用的结果是报错,为什么呢?
this指向问题
console.log(obj.valueOf()); // this -> obj
console.log(x.get('valueOf')()); // this -> 全局,严格模式下会指向undefined
// 因为在get函数里还没调用valueOf方法就已经return了-> obj[y]
// 也就是说通过拿到valueOf函数本身,并使用该函数再次进行了调用,相当于以下代码
const valueOf = Object.prototype.valueOf;
valueOf();
所以该如何进行处理呢?
解决过程:
在原型对象上设置一个访问器属性,只要访问这个属性,就相当于运行get函数。
Object.defineProperty(Object.prototype, 'abc', {
get() {
return this; // this -> 谁用指向谁 -> obj
},
});
// obj本身没有abc 所以会去原型上查找 此时abc是一个函数 调用这个函数于是得到obj本身
console.log(x.get('abc')) // { a: 1, b: 2 }
//通过这个漏洞可以直接在外部拿到obj内部的值并对其进行修改
let tmpObj = x.get('abc');
tmpObj.a = 'one';
tmpObj.b = 'two';
tmpObj.c = '我是新增的c';
console.log(x.get('a')); //one
console.log(x.get('b')); //two
console.log(x.get('c')); //我是新增的c
修复漏洞:
方法1:在函数内进行验证,判断传入的参数是否为obj本身的属性。
const x = (() => {
const obj = {
a: 1,
b: 2,
};
return {
get: y => {
if (obj.hasOwnProperty(y)) {
return obj[y];
}
return undefined;
}
};
})();
方法2:如果obj不需要使用到原型上的任何东西,可以直接将原型对象设为空。
const x = (() => {
const obj = {
a: 1,
b: 2,
};
Object.setPrototypeOf(obj, null);
return {
get: y => obj[y]
};
})();