2

闭包外的旧式 JavaScriptvar声明是全局的(顶级范围),可以在浏览器中从window对象访问。例如,声明var x = 3;可以使用window['x'].

给定声明的名称(字符串),您如何类似地访问constlet声明?

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

对于上面的例子,你如何获得值721“y”和“z”而不是undefined

摆弄代码:
https ://jsfiddle.net/g78ah6we/

编辑(为清楚起见添加了注释):
1. 虽然不是典型的,但有一些用例,例如在中,有必要仅根据声明的名称访问声明。
2. 只需要读取权限(不会修改任何声明)。
3.window提到对象只是为了展示旧的方式,但这个问题实际上并不是关于使用window对象(或global对象)。

4

3 回答 3

3

使用间接调用eval

可以使用间接调用来访问全局const和定义。即生成逗号分隔表达式的结果或首先将其分配给变量。如果语法访问不是直接访问内置函数,则它是间接访问,并且间接访问在全局范围内执行。letevalevaleval

您也可以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 的间接调用将一个letor变量添加到全局范围:它们是块级声明,并且代码评估被认为是一个块 - 所以当(间接调用)返回时,声明将被丢弃。constevaleval

PS。这是一个技术答案。是的,我之前听说过“eval 是邪恶的”一到三遍。


对于仅使用硬编码变量名称字符串的读取访问(以防止代码插入),您可以使用以下模式:

 (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不是那么容易)或至少在 try/catch 块中访问以捕获使用未声明的标识符或使用之前的名称声明初始化。constclass

就我个人而言,我建议通常寻找替代使用全局变量名称字符串的方法。myLibrary.data想到在库上提供静态名称空间对象(例如)并处理作为对象属性名称的字符串值,或者在库调用中包括选项对象参数。

于 2018-05-01T05:06:32.413 回答
0

let和都是const块作用域。

相反,没有var关键字的变量声明会在最外层的函数范围气泡中创建变量。在浏览器中,最外层的功能范围由window对象控制。

window对象不控制的是最外面的块范围。

如果您的代码在无法访问window[nn]模式中的变量的情况下无法工作,那么肯定存在设计问题。

于 2018-05-01T05:21:23.880 回答
0

我已将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

笔记:

  1. 请注意,identifierPattern正则表达式非常简单(不处理所有有效字符并在保留字上绊倒......正如 traktor53 指出的那样,“这比你想象的要复杂”)。
  2. 更改'number''object'或适合您需要的任何内容(为简单起见,我在原始问题中使用了带有数字的示例,但我的实际用例实际上是在寻找对象)。

摆弄代码:
https ://jsfiddle.net/g78ah6we/6/
截屏

于 2018-05-05T17:54:25.113 回答