window [name]等效于动态访问const和let声明

时间:2018-05-01 04:04:48

标签: javascript browser const variable-declaration window-object

闭包之外的旧式JavaScript var声明是全局的(顶级作用域),可以在浏览器中从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

对于上面的示例,如何为“y”和“z”而不是7获取值21undefined

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

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

3 个答案:

答案 0 :(得分:1)

使用间接调用eval

可以使用对const的间接调用来访问全局leteval定义。这是使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将letconst变量添加到全局范围:它们是块级声明,并且代码eval计算被视为一个块 - 所以当(间接调用)eval返回时,会丢弃声明。

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间接调用的替代方法。然而,差异在很大程度上是语义上的而不是一个比另一个好。

问题

如果全局变量名称(varletconstclass标识符)来自用户输入,则应该检查其是否有效({{3}或者至少在try / catch块中访问,以便在初始化之前捕获未使用的标识符或使用名称声明。

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

答案 1 :(得分:0)

letconst都是块范围的。

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

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

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

答案 2 :(得分: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指出的那样,"This is more complicated than you might think")。
  2. 'number'更改为'object'或任何适合您需求的内容(为了简单起见,我使用带数字的示例,但我的实际用例实际上是查找对象)。
  3. 摆弄代码:
    https://jsfiddle.net/g78ah6we/6/
    screen shot