闭包的一个漏洞

如何在不改变原代码的情况下 修改obj对象

const x = (() => {
    const obj = {
        a: 1,
        b: 2,
    };
    return {
        get: y => obj[y]
    };
})();

代码分析: 

首先分析这几行代码究竟是什么意思又包含了哪些东西?

  1. 一个IIFE立即执行函数
  2. 这个立即执行函数里包含了一个obj对象
  3. 该函数又返回了另一个对象
  4. 该对象有一个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]
    };
})();
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值