JavaScript模块模式变量存储疑问:timesRun外部获取值始终为0
问题:模块模式下外部无法获取更新后的变量值
先看这段JavaScript模块模式代码:
const Formatter = (function() { let timesRun = 0; const log = (message) => console.log(`[${Date.now()}] Logger: ${message}`); const setTimesRun = () => { log("Setting times run"); ++timesRun; console.log(timesRun); } const makeUppercase = (text) => { log("Making uppercase"); setTimesRun(); return text.toUpperCase(); }; return { makeUppercase, timesRun, } })() console.log(Formatter.makeUppercase("tomek")); console.log(Formatter.timesRun); console.log(Formatter.makeUppercase("foo")); console.log(Formatter.timesRun); console.log(Formatter.makeUppercase("hey")); console.log(Formatter.timesRun);
调用Formatter.makeUppercase()时,内部的timesRun能正常递增,控制台打印的内部值是对的,但外部通过Formatter.timesRun获取到的始终是0,搞不懂为什么内部和外部的数值不一样,也不清楚这个变量到底存在哪里。
原因与解决办法
问题根源
你在返回的对象里直接写timesRun,本质是把当时的timesRun值(也就是0)复制了一份放到返回的对象里——因为timesRun是基本类型(数字),赋值是值传递,和闭包里的timesRun不是同一个引用。
闭包里的timesRun存在于立即执行函数(IIFE)的私有作用域中,外部无法直接访问;而返回对象里的timesRun只是初始值的一个静态副本,后续闭包里的timesRun更新时,这个副本不会同步变化。
解决办法
方法1:新增getter函数
在返回的对象里定义一个获取最新值的函数,每次调用都去闭包里拿当前的timesRun:
const Formatter = (function() { let timesRun = 0; const log = (message) => console.log(`[${Date.now()}] Logger: ${message}`); const setTimesRun = () => { log("Setting times run"); ++timesRun; console.log(timesRun); } const makeUppercase = (text) => { log("Making uppercase"); setTimesRun(); return text.toUpperCase(); }; return { makeUppercase, getTimesRun: () => timesRun, } })() console.log(Formatter.makeUppercase("tomek")); console.log(Formatter.getTimesRun()); // 调用方法获取最新值 console.log(Formatter.makeUppercase("foo")); console.log(Formatter.getTimesRun()); console.log(Formatter.makeUppercase("hey")); console.log(Formatter.getTimesRun());
方法2:用ES6 getter语法
让返回对象的timesRun成为一个动态访问器属性,每次访问时自动获取闭包里的最新值:
const Formatter = (function() { let timesRun = 0; const log = (message) => console.log(`[${Date.now()}] Logger: ${message}`); const setTimesRun = () => { log("Setting times run"); ++timesRun; console.log(timesRun); } const makeUppercase = (text) => { log("Making uppercase"); setTimesRun(); return text.toUpperCase(); }; return { makeUppercase, get timesRun() { return timesRun; } } })() console.log(Formatter.makeUppercase("tomek")); console.log(Formatter.timesRun); // 直接访问即可获取最新值 console.log(Formatter.makeUppercase("foo")); console.log(Formatter.timesRun); console.log(Formatter.makeUppercase("hey")); console.log(Formatter.timesRun);
两种方法的核心逻辑一致:每次外部需要获取值时,都去访问闭包里的原始变量,而不是依赖初始的静态副本。
内容的提问来源于stack exchange,提问作者Metal
相关产品推荐
相关产品推荐

