Note: this question is a follow up of the recently asked question JavaScript: automatic getters and setters in closures without eval? .
The gist of that question was as follows: "How can one automatically provide getters and setters for scoped variables in a closure - without the use of the eval statement". There the poster, provided code demonstrating how to do so with eval and the user gave the following answer which does not require eval:
function myClosure() {
var instance = {};
var args = Array.prototype.slice.call(arguments);
args.forEach(function(arg) {
instance[arg] = function(d) {
if (!arguments.length) return arg;
arg = d;
return instance;
};
})
return instance;
};
This question is about how to have default values for the scoped variables which are to be set / get with the above function.
If we simply add a default value to the variable v3 we get the following:
function myClosure() {
var v3 = 2
var instance = {};
var args = Array.prototype.slice.call(arguments);
args.forEach(function(arg) {
instance[arg] = function(d) {
if (!arguments.length) return arg;
arg = d;
return instance;
};
})
return instance;
}
var test = myClosure("v1", "v2", "v3") // make setters/getters for all vars
test.v1(16).v2(2) // give new values to v1, v2
console.log(test.v1() + test.v2() + test.v3()) // try to add with default v3
// 18v3
I was not expecting that.
So how can I provide a default value to the variables?
Note: please build off the following implementation which generates the getters / setters on initialization (allowing the code author to pre-define all variables which should have getters and setters)
function myClosure() {
var instance = function () {};
var publicVariables =['v1', 'v2', 'v3']
function setup() {
var args = Array.prototype.slice.call(arguments);
// if called with a list, use the list, otherwise use the positional arguments
if (typeof args[0] == 'object' && args[0].length) { args = args[0] }
args.forEach(function(arg) {
instance[arg] = function(d) {
if (!arguments.length) return arg;
arg = d;
return instance;
};
})
}
setup(publicVariables)
// setup('v1', 'v2', 'v3') also works
return instance;
}
var test = myClosure()
test.v1(16).v2(2)
console.log(test.v1() + test.v2() + test.v3())
Question:
How to use default values in this set up (above code block) with automatic getters and setters?
Do you mean something like this:
function myClosure(...vars) {
const instance = {};
vars.forEach(varArg => {
let name = undefined;
let value = undefined;
if (typeof varArg == 'string')
{
name = varArg;
}
else
{
name = Object.keys(varArg)[0];
value = varArg[name];
}
instance[name] = function(d) {
if (!arguments.length) return value;
value = d;
return instance;
};
})
return instance;
}
const test = myClosure(
{ "v1": 1 },
"v2",
{ "v3": 3 },
);
// Print some defaults.
console.log(test.v1());
console.log(test.v2());
test.v1(16).v2(42) // give new values to v1, v2
console.log(test.v1(), test.v2(), test.v3())
Proxies, for the heck of it.
function myClosure(...vars) {
const instance = vars.reduce((obj, { name, value }) => {
obj[name] = value;
return obj;
}, {});
let proxy;
const handler = {
get: function(target, prop) {
return (...args) => {
if (args.length == 0)
return instance[prop];
instance[prop] = args[0];
return proxy;
};
}
};
proxy = new Proxy(instance, handler);
return proxy;
}
const test = myClosure(
{ name: "v1", value: 1 },
{ name: "v2" },
{ name: "v3", value: 3 }
);
// Print some defaults.
console.log(test.v1());
console.log(test.v2());
console.log(test.vNew());
test.v1(16).v2(42).vNew(50); // give new values to some variables.
console.log(test.v1(), test.v2(), test.v3(), test.vNew())
The gist of that question was as follows: "How can one automatically provide getters and setters for scoped variables in a closure - without the use of the eval statement". There the poster, provided code demonstrating how to do so with eval and the user gave an answer which does not require eval.
No, you cannot do without eval. All the answers here that don't use any form of eval do not access scoped variables, but rather just plain properties - or they create their own local variables.
Providing a default value is rather simple with that:
function myClosure(...args) {
var instance = {v3: 2};
// ^^^^^ not a `var`
for (const arg of args) {
let val = instance[arg];
instance[arg] = function(d) {
if (!arguments.length) return val;
val = d;
return instance;
};
}
return instance;
}
Note: I am posting my own answer for reference only. I will not be marking this as the answer to the question.
Building off the answer provided by @H.B., I update the answer in the following ways:
- getters and setters are made on initialization of the closure itself
- make the getter and setter production function a bit more messy to allow for more lazy definition of variables
instance is now a function, not an object
function myClosure() { var instance = function () { console.log(this.v1(), this.v2(), this.v3()) }; var publicVariables =[ 'v1', 'v2', {'v3': 3} ] function setup() { var args = Array.prototype.slice.call(arguments); // if called with a list, use the list, // otherwise use the positional arguments if (typeof args[0] == 'object' && args[0].length) { args = args[0] } args.forEach(function(arg) { var name, value if(typeof arg == 'object') { name = Object.keys(arg)[0] value = arg[name] } else { name = arg value = undefined } instance[name] = function(d) { if (!arguments.length) return value; value = d; return instance; }; // end instance function }) // end for each } // end setup setup(publicVariables) return instance; } var test = myClosure().v1(10).v2(2) console.log(test.v1(), test.v2(), test.v3()) test.v1(20).v3(1) console.log(test.v1(), test.v2(), test.v3())
来源:https://stackoverflow.com/questions/49474370/javascript-default-arguments-in-automatic-getters-and-setters-in-closures-witho