JavaScript:如何无需eval实现闭包中带默认参数的自动Getter和Setter
嘿,很高兴看到你在之前那个无eval闭包Getter/Setter方案的基础上继续深挖!咱们直接来搞定默认参数的支持,分几种常见场景给你拆解~
为无eval闭包Getter/Setter添加默认参数支持
先快速回顾下之前的核心思路:通过Object.defineProperty在外部访问对象上绑定闭包变量的Getter/Setter,完全避开eval,靠作用域链实现变量访问。接下来我们给这个方案加上默认参数的能力。
场景1:Getter返回默认值(变量未初始化/为undefined时)
如果闭包里的变量可能未初始化,我们希望Getter在读取时返回预设的默认值,而不是undefined。只需要给创建Accessor的函数加一个defaultValue参数即可:
function createAccessor(obj, propName, targetScope, defaultValue) { Object.defineProperty(obj, propName, { get() { // 使用空值合并运算符,仅当变量为undefined/null时返回默认值 return targetScope[propName] ?? defaultValue; }, set(value) { targetScope[propName] = value; }, enumerable: true }); } // 使用示例 function createClosureWithAccessors() { let foo; // 未初始化的变量 let bar = 42; const accessors = {}; // 给foo设置默认值'fallback_foo',bar不设置默认 createAccessor(accessors, 'foo', { foo }, 'fallback_foo'); createAccessor(accessors, 'bar', { bar }); return accessors; } const accessors = createClosureWithAccessors(); console.log(accessors.foo); // 输出 'fallback_foo' accessors.foo = 'custom_value'; console.log(accessors.foo); // 输出 'custom_value'
场景2:Setter支持默认参数(传undefined时使用默认值)
如果希望调用Setter时,若传入undefined就自动使用预设默认值(类似函数参数的默认值逻辑),可以扩展配置项,支持静态默认值或动态生成的默认值:
function createAccessor(obj, propName, targetScope, { getDefault, setDefault } = {}) { Object.defineProperty(obj, propName, { get() { // Getter默认值支持静态值或函数(动态生成) return targetScope[propName] ?? (typeof getDefault === 'function' ? getDefault() : getDefault); }, set(value) { // 仅当传入值为undefined时,使用setDefault const finalValue = value !== undefined ? value : (typeof setDefault === 'function' ? setDefault() : setDefault); targetScope[propName] = finalValue; }, enumerable: true }); } // 使用示例 function createClosureWithAccessors() { let foo = 'initial_foo'; let count = 0; const accessors = {}; // 给foo设置静态默认值 createAccessor(accessors, 'foo', { foo }, { setDefault: 'default_foo' }); // 给count设置动态默认值(基于当前count值+1) createAccessor(accessors, 'count', { count }, { setDefault: () => count + 1 }); return accessors; } const accessors = createClosureWithAccessors(); accessors.foo = undefined; // 使用setDefault console.log(accessors.foo); // 输出 'default_foo' accessors.count = undefined; // 调用动态默认函数 console.log(accessors.count); // 输出 1
场景3:函数式Getter/Setter支持默认参数
如果你更倾向于用函数调用的方式(比如accessors.foo()读取,accessors.foo(newVal)设置),可以把Accessor改成函数形式,更贴近函数参数默认值的写法:
function createFunctionalAccessor(obj, propName, targetScope, defaultValue) { obj[propName] = function(value) { if (arguments.length === 0) { // 无参数时作为Getter,返回当前值或默认值 return targetScope[propName] ?? defaultValue; } else { // 有参数时作为Setter,传undefined则用默认值 const finalValue = value !== undefined ? value : defaultValue; targetScope[propName] = finalValue; return this; // 支持链式调用 } }; } // 使用示例 function createClosureWithAccessors() { let foo = 'initial_foo'; const accessors = {}; createFunctionalAccessor(accessors, 'foo', { foo }, 'default_foo'); return accessors; } const accessors = createClosureWithAccessors(); console.log(accessors.foo()); // 输出 'initial_foo' accessors.foo(undefined); // 设置为默认值 console.log(accessors.foo()); // 输出 'default_foo' accessors.foo('new_custom_val').foo(); // 链式调用,输出 'new_custom_val'
以上所有方案都完全避开了eval,靠作用域链和JS原生语法实现了默认参数的支持,你可以根据自己的业务场景选择对应的写法~
内容的提问来源于stack exchange,提问作者SumNeuron
相关产品推荐
相关产品推荐

