JS疑难杂症之闭包真的“闭”吗?

222 阅读3分钟

前言

近日,看到了这样一个笔试题目,题目如下:

var o = (function () {
    var obj = { a: 1, b: 2 };
    return { get: function (key) { return obj[key]; } }
})();

问:如何在不改变上述代码的情况下,修改obj对象

看到这里,你脑海里是不是翁翁的呀!!!

引发的思考

  1. 这里的考察点有哪些
  2. 如何解决

考察点

这里我想到的有以下几点:

  1. 原型与原型链的理解
  2. 闭包的理解
  3. JS定义属性枚举的相关api
  4. 对valueOf的一些理解
  5. 对this的一些理解

对于这些考察点的理解可自行查阅资料,这里就不过多赘述了

如何解决

我们都知道,在JS中,有一个函数,对于非基本类型可以直接返回本身,它就是原型链中的valueOf函数, 关于它的理解可参考mdn文档: valueOf概念

那么第一反应可能就是直接调用o.get('valueOf');

我们先来看下,valueOf输出的是个啥,代码如下:

console.log(o.get('valueOf'));

好了,来查看下运行结果:

于是,你开开开心心的写出如下代码:

var o = (function () {
    var obj = { a: 1, b: 2 };
    return { get: function (key) { return obj[key]; } }
})();

var obj = o.get('valueOf')();

console.log(obj);

好了,来运行看下结果:

看,它报错了,傻眼了吧。。。

这里,它就涉及到this指向问题了, 如果你对this有深入的理解, 应该会清楚上面方案里的this它指向了globalThis

那么,怎么办!!!

好了,接下来,我来提供下我想到的答案,可能不是最合适的,如果你有更合适的方案,也可以给出你的答案

这里就不废话了,直接上代码,如下:

var o = (function () {
    var obj = { a: 1, b: 2 };
    return { get: function (key) { return obj[key]; } }
})();

Object.defineProperty(Object.prototype, 'getProp', {
    get() {
        return this;
    }
});

var obj = o.get('getProp');

console.log(obj);

好了,先来看下上面代码的运行结果:

看,这里就能或获取到o里面的obj对象本身了,就可以对其进行修改了

在上面的代码中加上如下代码:


console.log('修改之前: ', obj);

obj.a = 'aaa';

obj.b = 'bbb';

console.log('修改之后: ', obj);

运行结果如下:

看,成功了!!!

思考

这个问题就是一个典型的闭包漏洞问题,那么我们在实际的开发中,你如果了解并知道这个问题,就可以轻松的避免此类问题的存在了,接下来我们来看看,如何避免:

方案一:将原型设置为null

. 通过Object.setPrototypeOf(target, value) 设置其原型对象为null

代码如下:

var o = (function () {
    var obj = { a: 1, b: 2 };
    Object.setPrototypeOf(obj, null); // 这里也可以直接给__proto__赋值
    return { get: function (key) { return obj[key]; } }
})();

方案二: 通过判断在访问非对象本身属性时,返回undefined

. 通过target.hasOwnProperty 判别是不是访问target本身上的属性,如果不是,直接返回undefined

代码如下:

var o = (function () {
    var obj = { a: 1, b: 2 };
    return {
         get: function (key) {
            if (obj.hasOwnProperty(key)) return obj[key];
            return undefined;
         }
    }
})();

这里或许不是最合适的方案,如果你有更好的欢迎提出,大家一起进步