如何在javascript中动态访问本地范围?
如果要动态使用全局函数和变量,可以使用:如何在javascript中动态访问本地范围?,javascript,scope,Javascript,Scope,如果要动态使用全局函数和变量,可以使用: window[functionName](window[varName]); 是否可以对局部范围内的变量执行相同的操作 这段代码工作正常,但目前使用eval,我正在考虑其他方法 var test = function(){ //this = window var a, b, c; //private variables var prop = function(name, def){ //this = windo
window[functionName](window[varName]);
是否可以对局部范围内的变量执行相同的操作
这段代码工作正常,但目前使用eval,我正在考虑其他方法
var test = function(){
//this = window
var a, b, c; //private variables
var prop = function(name, def){
//this = window
eval(name+ ' = ' + (def.toSource() || undefined) + ';');
return function(value){
//this = test object
if ( !value) {
return eval('(' + name + ')');
}
eval(name + ' = value;')
return this;
};
};
return {
a:prop('a', 1),
b:prop('b', 2),
c:prop('c', 3),
d:function(){
//to show that they are accessible via to methods
return [a,b,c];
}
};
}();
>>>test
Object
>>>test.prop
undefined
>>>test.a
function()
>>>test.a()
1 //returns the default
>>>test.a(123)
Object //returns the object
>>>test.a()
123 //returns the changed private variable
>>>test.d()
[123,2,3]
希望我没有过度简化,但是像使用对象这样简单的东西呢
var test = {
getValue : function(localName){
return this[localName];
},
setValue : function(localName, value){
return this[localName] = value;
}
};
>>> test.a = 123
>>> test.getValue('a')
123
>>> test.a
123
>>> test.setValue('b', 999)
999
>>> test.b
999
要回答您的问题,不,如果不使用
eval()
,就无法在本地范围内执行动态变量查找
最好的替代方法是将你的“范围”变成一个普通的对象[文字](即,“{}”
),然后把你的数据放在那里。不,就像上面说的那样。下面是一个如何在没有eval的情况下使用内部私有对象实现的示例
var test = function () {
var prv={ };
function prop(name, def) {
prv[name] = def;
return function(value) {
// if (!value) is true for 'undefined', 'null', '0', NaN, '' (empty string) and false.
// I assume you wanted undefined. If you also want null add: || value===null
// Another way is to check arguments.length to get how many parameters was
// given to this function when it was called.
if (typeof value === "undefined"){
//check if hasOwnProperty so you don't unexpected results from
//the objects prototype.
return Object.prototype.hasOwnProperty.call(prv,name) ? prv[name] : undefined;
}
prv[name]=value;
return this;
}
};
return pub = {
a:prop('a', 1),
b:prop('b', 2),
c:prop('c', 3),
d:function(){
//to show that they are accessible via two methods
//This is a case where 'with' could be used since it only reads from the object.
return [prv.a,prv.b,prv.c];
}
};
}();
我认为你实际上可以,甚至不用eval强> 我可能错了,如果我错了,请纠正我,但我发现如果私有变量在局部范围内声明为参数,而不是使用
var
,即:
函数(a,b,c){…
而不是
function(){var a,b,c;..
这意味着,如果在函数调用中为这些变量/参数指定了任何值,则这些变量/参数将与函数的参数
对象绑定在一起,即:
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...'
在这些情况下(它工作的地方),如果您以某种方式存储/保存被调用函数的参数
对象,则可以从参数
对象更改任何与参数相关的插槽,并且更改将自动反映在变量本身中,即:
// 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(',')
现在有了数组中的所有变量,我们现在可以知道每个函数的参数
插槽索引对应的变量名,并据此声明一个函数(在本例中是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);
});
最后,我们可以添加一个方法来按名称检索变量(默认情况下为all),如果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)];
});
}
prop和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,读取/写入函数的局部作用域变量是不可能的,但是如果这些变量是函数的参数,并且它们是给定值,则可以将这些变量标识符绑定到函数的
参数
对象,并从间接地读取/写入它们参数
对象本身。如果你要直接访问对象的局部变量,那么定义一个getter或setter有什么意义呢?没有。我只是在中添加了一个,以便用法与Annan的示例相同。我同意,这在实践中是完全愚蠢的。代码的原因是要模拟getter的方式并且setter以跨浏览器的方式工作。因此,我的原始代码不正确(现在已修复),因为变量prop()创建的属性对对象的其余部分不可用。您的方式允许直接访问和操纵属性。阅读其他人的代码是一个很好的理由。即使公开的“someProperty”没有告诉我某个对象是否要由任何旧函数使用,或者该对象的作者是否希望另一个对象更改它。我得到为什么我现在感到困惑,是因为你在命名变量abc以及返回的名称。你应该更改这些名称,这正是让我困惑的地方。无论如何,比我的答案更好的答案已经出现了,所以我现在只想遵从crescentfresh。@Gothdo你链接的问题是问一些不同的问题。variab他试图访问的是全局变量。公认的答案也使用全局变量。链接的问题应该更改。在函数本身中枚举局部变量怎么样?换句话说,像eval,但当你不知道局部变量是什么时…@Michael,对不起,我不明白你的意思。你能给我一个答案吗举个例子?或者甚至写下你自己的答案?我没有答案,我读到的所有东西似乎都表明这是不可能的…浏览器中的全局变量可以用类似于for(我在窗口中)的东西枚举
;如果函数中的局部变量可以这样做,那就太好了。@Michael,你能给我举个例子,说明什么时候这会有用,什么地方不能使用对象吗?在某些情况下,这对内省很有用,例如序列化闭包。