window[name] 相当于动态访问 const 和 let 声明
window[name] equivalent to dynamically access const and let declarations
闭包外的旧式 JavaScript var
声明是全局的(顶级范围)并且可以在浏览器中从 window
对象访问。例如,可以使用 window['x']
.
访问声明 var x = 3;
在给定声明名称(字符串)的情况下,您如何类似地访问 const
或 let
声明?
var x = 3;
const y = 7;
let z = 21;
console.log('x = ' + window['x']); //x = 3
console.log('y = ' + window['y']); //y = undefined
console.log('z = ' + window['z']); //z = undefined
对于上面的例子,如何得到 7
和 21
的 "y" 和 "z" 而不是 undefined
的值?
Fiddle 代码:
https://jsfiddle.net/g78ah6we/
编辑 (为清楚起见添加注释):
1. 虽然不典型,但有一些用例,例如在 library 中,有必要仅根据声明的名称访问声明。
2. 只需要读取权限(none 的声明将被修改)。
3.提到window
对象只是为了展示旧方法,但这道题实际上并不是关于使用window
对象(或global
对象)。
使用间接调用 eval
访问全局 const
和 let
定义可以使用对 eval
的间接调用来完成。那就是使 eval
成为逗号分隔表达式的结果或首先将其分配给变量。如果句法访问不是直接访问内置 eval
函数,那么它是间接访问,并且间接访问在全局范围内执行。
您也可以通过构建脚本来设置全局let
变量来执行设置操作
"use strict";
let myVar = "global variable myVar";
console.log( myVar);
(function myLibrary() {
const myVar = "local variable myVar";
const indirectEval = eval;
var varName = "myVar";
console.log( eval(varName)); // direct call uses local scope
console.log( indirectEval(varName)); // indirect call uses global scope
var result = "\"updated global variable even though shadowed\"";
var js = varName + '=' + result;
indirectEval(js);
// but trying to define a new let variable doesn't attach to global scope
var js2 ='let letVar2 = "let variable two"';
indirectEval( js2);
})();
console.log( myVar)
console.log( "letVar2: " + typeof letVar2);
你不能做的是使用对 eval 的间接调用将 let
或 const
变量添加到全局范围:它们是块级声明并且代码 eval
计算被认为是一个块 - 因此当(间接调用)eval
returns.
时声明被丢弃
PS。这是一个技术答案。是的,我以前听说过 "eval is evil" 一到三次。
对于仅使用硬编码变量名称字符串的读取访问(以防止代码插入),您可以使用模式:
(0,eval)("identifierString");
例如:
var x = 3;
const y = 7;
let z = 21;
{
const y = "shadow"
let z = 42;
console.log('x = ' + (0,eval)('x')); //x = 3
console.log('y = ' + (0,eval)('y')); //y = 7
console.log('z = ' + (0,eval)('z')); //z = 21
}
间接调用与直接调用 eval
直接调用 eval
只获得调用的函数范围内未隐藏的全局变量的值。这可能会限制变量名称的选择,或者可以在库中进行调用的位置。
间接调用在全局范围内执行,并且可以获取全局变量的值,而无需考虑库中的名称阴影。
从源文本创建一个新的 Function
对象并调用它,可能是在网页中使用对 eval
的间接调用的替代方法。然而,差异主要是语义上的,而不是一个比另一个更好。
问题
如果全局变量名称(var
、let
、const
或 class
标识符)来自用户输入,则确实应该检查其有效性(not all that easy) 或至少在 try/catch 块中访问以捕获使用未声明的标识符或在初始化之前使用名称声明。
就我个人而言,我建议寻找一般情况下使用全局变量名称字符串的替代方法。在库中提供静态名称 space 对象(例如 myLibrary.data
)并处理作为对象名称 属性 的字符串值,或者在库调用中包含选项对象参数。
let
和 const
都是块范围的。
相比之下,不带 var
关键字的变量声明在最外层的函数作用域气泡中创建变量。在浏览器中,最外层的功能范围由 window
对象控制。
window
对象不控制的是最外层的块作用域。
如果您的代码在无法访问 window[nn]
模式中的变量的情况下无法运行,则肯定存在设计问题。
我已将 traktor53 的回答标记为已接受并点赞,因为它包含解决方案的技术核心。
如果它对任何人都有帮助,这里有一个解决方案包含在一个函数中,可以防止在声明中执行代码。
var x = 3;
const y = 7;
let z = 21;
const malware = 'alert("Game over.");';
function getDeclaration(name) {
var identifierPattern = /^[_$a-zA-Z][_$a-zA-Z0-9]*$/;
var topLevelGet = (null, eval);
return identifierPattern.test(name) && topLevelGet('typeof ' + name) === 'number' ?
topLevelGet(name) : null;
}
console.log(getDeclaration('x')); //output: 3
console.log(getDeclaration('y')); //output: 7
console.log(getDeclaration('z')); //output: 21
console.log(getDeclaration('bogus')); //output: null
console.log(getDeclaration('malware')); //output: null
console.log(getDeclaration('if')); //EXCEPTION: unexpected keyword
备注:
- 请注意,
identifierPattern
正则表达式非常简单(不处理所有有效字符并在保留字上出错...正如 traktor53 指出的那样,"This is more complicated than you might think")。
- 将
'number'
更改为 'object'
或任何适合您需要的内容(为简单起见,在原始问题中我使用了带有数字的示例,但我的实际用例实际上是查找对象)。
Fiddle 代码:
https://jsfiddle.net/g78ah6we/6/
闭包外的旧式 JavaScript var
声明是全局的(顶级范围)并且可以在浏览器中从 window
对象访问。例如,可以使用 window['x']
.
var x = 3;
在给定声明名称(字符串)的情况下,您如何类似地访问 const
或 let
声明?
var x = 3;
const y = 7;
let z = 21;
console.log('x = ' + window['x']); //x = 3
console.log('y = ' + window['y']); //y = undefined
console.log('z = ' + window['z']); //z = undefined
对于上面的例子,如何得到 7
和 21
的 "y" 和 "z" 而不是 undefined
的值?
Fiddle 代码:
https://jsfiddle.net/g78ah6we/
编辑 (为清楚起见添加注释):
1. 虽然不典型,但有一些用例,例如在 library 中,有必要仅根据声明的名称访问声明。
2. 只需要读取权限(none 的声明将被修改)。
3.提到window
对象只是为了展示旧方法,但这道题实际上并不是关于使用window
对象(或global
对象)。
使用间接调用 eval
访问全局 const
和 let
定义可以使用对 eval
的间接调用来完成。那就是使 eval
成为逗号分隔表达式的结果或首先将其分配给变量。如果句法访问不是直接访问内置 eval
函数,那么它是间接访问,并且间接访问在全局范围内执行。
您也可以通过构建脚本来设置全局let
变量来执行设置操作
"use strict";
let myVar = "global variable myVar";
console.log( myVar);
(function myLibrary() {
const myVar = "local variable myVar";
const indirectEval = eval;
var varName = "myVar";
console.log( eval(varName)); // direct call uses local scope
console.log( indirectEval(varName)); // indirect call uses global scope
var result = "\"updated global variable even though shadowed\"";
var js = varName + '=' + result;
indirectEval(js);
// but trying to define a new let variable doesn't attach to global scope
var js2 ='let letVar2 = "let variable two"';
indirectEval( js2);
})();
console.log( myVar)
console.log( "letVar2: " + typeof letVar2);
你不能做的是使用对 eval 的间接调用将 let
或 const
变量添加到全局范围:它们是块级声明并且代码 eval
计算被认为是一个块 - 因此当(间接调用)eval
returns.
PS。这是一个技术答案。是的,我以前听说过 "eval is evil" 一到三次。
对于仅使用硬编码变量名称字符串的读取访问(以防止代码插入),您可以使用模式:
(0,eval)("identifierString");
例如:
var x = 3;
const y = 7;
let z = 21;
{
const y = "shadow"
let z = 42;
console.log('x = ' + (0,eval)('x')); //x = 3
console.log('y = ' + (0,eval)('y')); //y = 7
console.log('z = ' + (0,eval)('z')); //z = 21
}
间接调用与直接调用 eval
直接调用 eval
只获得调用的函数范围内未隐藏的全局变量的值。这可能会限制变量名称的选择,或者可以在库中进行调用的位置。
间接调用在全局范围内执行,并且可以获取全局变量的值,而无需考虑库中的名称阴影。
从源文本创建一个新的 Function
对象并调用它,可能是在网页中使用对 eval
的间接调用的替代方法。然而,差异主要是语义上的,而不是一个比另一个更好。
问题
如果全局变量名称(var
、let
、const
或 class
标识符)来自用户输入,则确实应该检查其有效性(not all that easy) 或至少在 try/catch 块中访问以捕获使用未声明的标识符或在初始化之前使用名称声明。
就我个人而言,我建议寻找一般情况下使用全局变量名称字符串的替代方法。在库中提供静态名称 space 对象(例如 myLibrary.data
)并处理作为对象名称 属性 的字符串值,或者在库调用中包含选项对象参数。
let
和 const
都是块范围的。
相比之下,不带 var
关键字的变量声明在最外层的函数作用域气泡中创建变量。在浏览器中,最外层的功能范围由 window
对象控制。
window
对象不控制的是最外层的块作用域。
如果您的代码在无法访问 window[nn]
模式中的变量的情况下无法运行,则肯定存在设计问题。
我已将 traktor53 的回答标记为已接受并点赞,因为它包含解决方案的技术核心。
如果它对任何人都有帮助,这里有一个解决方案包含在一个函数中,可以防止在声明中执行代码。
var x = 3;
const y = 7;
let z = 21;
const malware = 'alert("Game over.");';
function getDeclaration(name) {
var identifierPattern = /^[_$a-zA-Z][_$a-zA-Z0-9]*$/;
var topLevelGet = (null, eval);
return identifierPattern.test(name) && topLevelGet('typeof ' + name) === 'number' ?
topLevelGet(name) : null;
}
console.log(getDeclaration('x')); //output: 3
console.log(getDeclaration('y')); //output: 7
console.log(getDeclaration('z')); //output: 21
console.log(getDeclaration('bogus')); //output: null
console.log(getDeclaration('malware')); //output: null
console.log(getDeclaration('if')); //EXCEPTION: unexpected keyword
备注:
- 请注意,
identifierPattern
正则表达式非常简单(不处理所有有效字符并在保留字上出错...正如 traktor53 指出的那样,"This is more complicated than you might think")。 - 将
'number'
更改为'object'
或任何适合您需要的内容(为简单起见,在原始问题中我使用了带有数字的示例,但我的实际用例实际上是查找对象)。
Fiddle 代码:
https://jsfiddle.net/g78ah6we/6/