window [name] эквивалентно динамическому доступу к объявлениям const и let - PullRequest
0 голосов
/ 01 мая 2018

Объявление старого стиля JavaScript var вне замыкания является глобальным (область верхнего уровня) и доступно в браузере из объекта window. Например, объявление var x = 3; можно получить с помощью window['x'].

Как вы также получаете доступ к объявлению 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?

Скрипка с кодом:
https://jsfiddle.net/g78ah6we/

Правки (примечания добавлены для ясности):
1. Хотя это не типично, существуют варианты использования, например, из библиотеки , при которой необходимо получить доступ к объявлению только на основе имени объявления.
2. Необходим только доступ на чтение (ни одно из объявлений не будет изменено).
3. Объект window упоминается просто для того, чтобы показать старый способ, но на самом деле этот вопрос не касается использования объекта window (или объекта global).

Ответы [ 3 ]

0 голосов
/ 01 мая 2018

Обе let и const имеют блочную область.

Напротив, объявления переменных без ключевого слова var создают переменные в самой внешней функциональной области действия. В браузерах внешняя функциональная область управляется объектом window.

То, что window не контролирует объект, является самой внешней областью блока.

Если ваш код не работает без возможности доступа к переменным в шаблоне window[nn], в нем определенно есть проблема проектирования.

0 голосов
/ 05 мая 2018

Я отметил 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/
screen shot

0 голосов
/ 01 мая 2018

Использование косвенных вызовов на 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);

Чего вы не можете сделать, так это добавить переменную let или const в глобальную область, используя косвенный вызов eval: они являются объявлениями уровня блока, а код, который оценивает eval, считается блоком - поэтому объявления отбрасываются при возврате (косвенный вызов) eval.

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 идентификатор) поступает из пользовательского ввода, его действительно следует проверить на достоверность ( не все так просто ) или по крайней мере доступ к нему внутри блока try / catch для отлова использованных необъявленных идентификаторов или использования объявлений имен перед инициализацией.

Лично я бы порекомендовал найти альтернативы использованию строк имен глобальных переменных в целом. На ум приходят предоставление статического объекта пространства имен в библиотеке (например, myLibrary.data) и обработка строковых значений, которые являются именами свойств объекта, или включение параметров объекта опции в вызовы библиотеки.

...