一道关于闭包内存泄露的的问题

83 阅读2分钟

今天看到一个很有意思的面试题,怎么不更改内部代码来更改函数内部的obj,这就是典型的利用闭包来保护局部变量,首先我们想到可以用原型修改,但是怎么做呢? 咱们一步一步来开哈

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

首先怎么可以通过getValue.get('a')来拿到a的值也就是obj['a'],通过这种方式咱们也可以拿到b。 那么咱们怎么修改它呢?

第一步咱们来看可以通过getValue.get可以拿到这个对象的任意的值,那咱们是不是也可以拿到这个对象的valueOf,咱们都知道valueOf返回的就是这个对象本身,咱们来试试getValue.get('valueOf'),咱们来打印一下看看它是什么

image.png

嗯?这不是就是valueOf的方法吗?我直接执行一下不就可以拿到这个对象并修改它了吗,后来无知的就直接执行了一下getValue.get('valueOf')()

image.png 震惊!!!为啥会报错,仔细一想,哦,这里边还有个this指向的问题,咱们这样直接执行,this不是指向的obj,这个this指向的是全局,严格模式下指向的undefined,举个例子

let valueOf = Object.prototype.valueOf;
valueOf();

跟这样执行没区别

image.png 要解决这个问题咱们在 get: function (key) {return obj[key]();}这里指向一下不就好了,但是要求不更改内部代码所以咱们可不可通过设置原型的办法更改它呀

Object.defineProperty(Object.prototype, 'c', {
    get() {
	return this;
    }
});
let c = getValue.get('c');
console.log(c);

这样不就拿到这个对象了吗?obj里边没有c这个属性,会在原型上查找,咱们不就拿到这个对象了吗

image.png 然后咱们进行更改

Object.defineProperty(Object.prototype, 'c', {
    get() {
	return this;
    }
});
let c = getValue.get('c');
c.a = 1234;
c.b = '我被修改了';
console.log(getValue.get('a'));
console.log(getValue.get('b'));

image.png 看咱们把它内部的obj属性更改了,那么咱们怎么避免这个问题呢? 咱们可以这样

let getValue = (function () {
     let obj = {
          a: 1,
          b: 2
     };
    return {
       get: function (key) {
         if (obj.hasOwnProperty(key)) {
		return obj[key];
           }
           return null;
      }
    };
})();

通过判断咱们获取的是不是对象本身的属性 hasOwnProperty,是就返回,否则返回null,咱们再执行修改后的代码就会报错

image.png null身上没有a的属性 或者咱们通过Object.setPrototypeOf(obj, null);把obj的原型设置为null。就可以避免这个问题了

附完整代码

let getValue = (function () {
        let obj = {
            a: 1,
            b: 2
	};
	Object.setPrototypeOf(obj, null);
	return {
            get: function (key) {
		// if (obj.hasOwnProperty(key)) {
		// 	return obj[key];
		// }
		// return null;
		return obj[key];
            }
	};
})();

// let a = getValue.get('a');
// console.log(a);
// let b = getValue.get('b');
// console.log(getValue.get('valueOf')());
// let valueOf = Object.prototype.valueOf;
// valueOf();
Object.defineProperty(Object.prototype, 'c', {
	get() {
		return this;
	}
});
let c = getValue.get('c');
c.a = 1234;
c.b = '我被修改了';
console.log(getValue.get('a'));
console.log(getValue.get('b'));