闭包的一个漏洞
非原创:记录+自己的理解
这道题目需要掌握闭包以及原型和原型链的相关知识
题目
const o = (function () {
let obj = { a: 1, b: 2 };
return {
get(k) {
return obj[k];
},
};
})();
// 如何在不改变上面代码的情况下 修改obj对象
题目分析
题目主要利用了闭包创建私有空间,可以看出其目的旨在让外界只能通过提供的get方法来读取obj对象里的属性
o.get('a'); // 1
o.get('b'); // 2
但是这道题的漏洞就在于,obj既然是一个对象,我们通过get方法除了可以访问到obj的自身属性之外,还可以通过原型链访问到其原型中的属性Object.prototype.xxx
于是我们自然而然想到了在Object.prototype中有没有一个属性让我们可以获取到obj呢
尝试1
const a = { hello: "world" };
console.log(a.valueOf()); // {hello: 'world'}
没错,就是Object.prototype.valueOf属性
console.log(o.get('valueOf')); // ƒ valueOf() { [native code] }
console.log(o.get('valueOf')()); // Uncaught TypeError: Cannot convert undefined or null to object at valueOf (<anonymous>)
为什么会报错?
分析可知是由于
this指向出现了问题
const v = Object.prototype.valueOf;
console.log(v); // ƒ valueOf() { [native code] }
v(); // Uncaught TypeError: Cannot convert undefined or null to object at valueOf (<anonymous>)
如果题目改为
const o = (function () {
let obj = { a: 1, b: 2 };
return {
get(k) {
return obj[k](); // 此处改动为方法调用
},
};
})();
则上述方法可行,我们可以拿到obj对象
console.log(o.get("valueOf")); // {a: 1, b: 2}
尝试2
既然Object.prototype中的既有属性不能满足我们的需求,那么我们就考虑给它添加一个属性来另辟蹊径
通过上面的尝试我们想到可以通过函数调用的this来获取
Object.defineProperty(Object.prototype, "xxx", {
get() {
return this;
},
});
// 其中 xxx 可以任意设置
console.log(o.get("xxx")); // {a: 1, b: 2}
结果
Object.defineProperty(Object.prototype, "xxx", {
get() {
return this;
},
});
let obj2 = o.get("xxx");
obj2.a = "hello world";
obj2.c = 3;
console.log(o.get("a")); // hello world
console.log(o.get("b")); // 2
console.log(o.get("c")); // 3
优化
const o = (function () {
let obj = { a: 1, b: 2 };
return {
get(k) {
if (obj.hasOwnProperty(k)) {
return obj[k];
}
},
};
})();
Object.defineProperty(Object.prototype, "xxx", {
get() {
return this;
},
});
let obj2 = o.get("xxx");
obj2.a = "hello world"; // Uncaught TypeError: Cannot set properties of undefined (setting 'a')
or
const o = (function () {
let obj = { a: 1, b: 2 };
Object.setPrototypeOf(obj, null);
return {
get(k) {
return obj[k];
},
};
})();
Object.defineProperty(Object.prototype, "xxx", {
get() {
return this;
},
});
let obj2 = o.get("xxx");
obj2.a = "hello world"; // Uncaught TypeError: Cannot set properties of undefined (setting 'a')