闭包返回数组/对象暴露其词法上下文:在JavaScript中处理的最佳方法



我想这可能是重复的,但我还找不到解释。

这是我的示例代码:

const makeCalendar = () => {
const calendar = {};
calendar.xmas = ['December', 25];
calendar.newYear = ['January', 1];
return (day) => calendar[day];
}
calendar = makeCalendar();
const xmasArray = calendar('xmas');
console.log(calendar('xmas')); // [ 'December', 25 ]
xmasArray[1]++;
console.log(calendar('xmas')); // [ 'December', 26 ]

由于xmasArray的元素是可变的,所以我可以更改makeCalendar()范围内的变量,从而破坏它返回的闭包。我发现解决这个问题的唯一方法是返回一个匿名数组[...calendar[day]](而不是calendar[day](,然后它会阻止makeCalendar()内部的访问。

我的问题是:这是处理这个问题的正确方法吗?有更好的方法吗?可能我没有正确理解发生了什么…

一个选项是使用Object.freeze来禁止分配给数组中的任何项目:

const makeCalendar = () => {
const calendar = {
xmas: ['December', 25],
newYear: ['January', 1]
};
Object.values(calendar).forEach(arr => Object.freeze(arr));
return (day) => calendar[day];
}
calendar = makeCalendar();
const xmasArray = calendar('xmas');
console.log(calendar('xmas'));
xmasArray[1]++;
console.log(calendar('xmas'));

请注意,这种尝试的分配将在严格模式下引发错误:

Uncaught TypeError: Cannot assign to read only property '1' of object '[object Array]
const makeCalendar = () => {
const calendar = {};
calendar.xmas = ['December', 25];
calendar.newYear = ['January', 1];
return (day) => JSON.parse(JSON.stringify(calendar))[day];
}

如果日历对象变得很大,但仍然有效,那就不是最好的了。具体来说,在这种情况下,即使是JSON.parse(JSON.stringify(calendar[day]))也可以工作。

诀窍是,进行解析和字符串化将创建原始对象的新副本,因此原始对象不会受到任何影响。当然,你可以使用其他方法来克隆对象,你可以在这里找到更多信息:在JavaScript中深度克隆对象的最有效方法是什么?

小提琴:http://jsfiddle.net/briosheje/317hg6fb/

最新更新