我想到你居然 排序 就可以了,即使不使用eval!
我可能错了,所以如果我错了,请纠正我,但我发现如果私有变量在本地范围内作为参数声明,而不是使用var
,即:
function (a, b, c) { ...
代替
function () { var a, b, c; ...
这意味着arguments
如果在函数调用中为它们提供了任何值,则这些变量/参数将与函数的对象绑定在一起,即:
function foo (bar) {
arguments[0] = 'changed...';
console.log(bar); // prints 'changed...'
bar = '...yet again!';
console.log(arguments[0]); // prints '..yet again!'
}
foo('unchanged'); // it works (the bound is created)
// logs 'changed...'
// logs '...yet again!'
foo(undefined); // it works (the bound is created)
// logs 'changed...'
// logs '...yet again!'
foo(); // it doesn't work if you invoke the function without the 'bar' argument
// logs undefined
// logs 'changed...'
在这些情况下(它工作的地方),如果您以某种方式存储/保存被调用函数的arguments
对象,那么您可以从arguments
对象更改任何与参数相关的插槽,更改将自动反映在变量本身中,即:
// using your code as an example, but changing it so it applies this principle
var test = function (a, b, c) {
//this = window
var args = arguments, // preserving arguments as args, so we can access it inside prop
prop = function (i, def) {
//this = window
// I've removed .toSource because I couldn't apply it on my tests
//eval(name+ ' = ' + (def.toSource() || undefined) + ';');
args[i] = def || undefined;
return function (value) {
//this = test object
if (!value) {
//return eval('(' + name + ')');
return args[i];
}
//eval(name + ' = value;');
args[i] = value;
return this;
};
};
return {
a: prop(0, 1),
b: prop(1, 2),
c: prop(2, 3),
d: function () {
// to show that they are accessible via to methods
return [a, b, c];
}
};
}(0, 0, 0);
如果您可以将值作为参数传递给函数这一事实让您感到烦恼,您始终可以将它与另一个匿名函数包装在一起,这样您就真的无法访问作为参数传递的第一个定义的值,即:
var test = (function () {
// wrapping the function with another anomymous one
return (function (a, b, c) {
var args = arguments,
prop = function (i, def) {
args[i] = def || undefined;
return function (value) {
if (!value) {
return args[i];
}
args[i] = value;
return this;
};
};
return {
a: prop(0, 1),
b: prop(1, 2),
c: prop(2, 3),
d: function () {
return [a, b, c];
}
};
})(0, 0, 0);
})();
完全动态访问示例
我们可以通过将函数本身 ( arguments.callee
) 作为字符串获取并使用正则表达式过滤其参数来将所有参数变量名称映射到数组中:
var argsIdx = (arguments.callee + '').replace(/function(\s|\t)*?\((.*?)\)(.|\n)*/, '$2').replace(/(\s|\t)+/g, '').split(',')
现在有了数组中的所有变量,我们现在可以知道每个函数的arguments
插槽索引对应的变量名称,然后声明一个函数(在我们的例子中是prop)来读/写变量:
function prop (name, value) {
var i = argsIdx.indexOf(name);
if (i === -1) throw name + ' is not a local.';
if (arguments.hasOwnProperty(1)) args[i] = value;
return args[i];
}
我们还可以动态地将每个变量添加为一个属性,就像在问题的示例中一样:
argsIdx.forEach(function (name, i) {
result[name] = prop.bind(null, name);
});
最后,我们可以添加一个方法来按名称检索变量(默认全部),如果true
作为第一个参数传递,它将返回带有所有变量的标识符的硬编码数组,以证明它们正在被更改:
function props (flgIdent) {
var names = [].slice.call(arguments.length > 0 ? arguments : argsIdx);
return flgIdent === true ? [a, b, c, d, e, f] : names.map(function (name) {
return args[argsIdx.indexOf(name)];
});
}
在props和props的功能,可以提供作为返回的对象内部方法,它到底会是这个样子:
var test = (function () {
return (function (a, b, c, d, e, f) {
var argsIdx = (arguments.callee + '').replace(/function(\s|\t)*?\((.*?)\)(.|\n)*/, '$2').replace(/(\s|\t)+/g, '').split(','),
args = arguments,
result = {
prop: function (name, value) {
var i = argsIdx.indexOf(name);
if (i === -1) throw name + ' is not a local.';
if (arguments.hasOwnProperty(1)) args[i] = value;
return args[i];
},
props: function (flgIdent) {
var names = [].slice.call(arguments.length > 0 ? arguments : argsIdx);
return flgIdent === true ? [a, b, c, d, e, f] : names.map(function (name) {
return args[argsIdx.indexOf(name)];
});
}
};
args.length = argsIdx.length;
argsIdx.forEach(function (name, i) {
result[name] = result.prop.bind(null, name);
});
return result;
})(0, 0, 0, 0, 0, 0);
})();
结论
没有 eval 就不可能读/写函数的局部作用域变量,但是如果这些变量是函数的参数并且如果它们被赋予了值,您可以将这些变量标识符绑定到函数的arguments
对象,并从对象本身间接读/写到它们arguments
.