Статьи

ECMAScript 6 и область действия блока

До сегодняшнего дня JavaScript поставляется с областью действия на уровне функций для переменных и функций. Эта причуда часто спотыкает начинающих, которые уже знакомы с другим языком фигурных скобок. С ECMAScript 6 ситуация изменится с появлением хорошо понятной области блока .

Область действия на уровне функций приводит к ситуации, называемой подъемом . Например, для этого кода:

function f() {
  doSomething();
  var a = 1;
}

что действительно происходит, это что-то вроде:

function f() {
  var a;
  doSomething();
  a = 1;
}

Часто знания о подъеме (или лексической среде в целом) используются в викторине или интервью. В следующем фрагменте те, кто не обладает пониманием, останутся озадаченными:

console.log('foo' in window); // true
var foo;

Для программистов, знакомых с C / C ++ / Java, использование varв неправильном месте может привести к множеству ошибок , среди которых утечка переменных . Является ли это намеренным или нет, часто не очищается из самого кода. Подобная двусмысленность может привести к ошибке и другим трудно прослеживаемым раздражениям:

for (var i = 0; i < 3; i++) {
  var j = i * i;
  console.log(j);
}
console.log(j); //  4

С выходом ECMAScript 6 мы можем решить эту проблему с помощью объявлений Let и Const , см. Раздел 12.2.1 проекта последней спецификации . Если переписать приведенный выше пример для использования let, он будет выглядеть следующим образом:

for (var i = 0; i < 3; i++) {
  let j = i * i;
  console.log(j);
}
console.log(j); // will throw

тогда его запуск выдаст ошибку:

ReferenceError: j не определено

Это типичное ожидание программиста, jограниченное этим конкретным фигурным блоком.

Его соучастник constведет себя примерно так же, за исключением того, что он должен быть инициализирован и только один раз . Это идеально подходит для хранения неизменяемого объекта. С обоими letи const, оптимистический статический анализатор кода может работать намного умнее, чтобы обнаружить шаблоны, которые могут вызвать проблемы и предупредить пользователя заблаговременно.

Когда мы можем начать использовать letи const? К счастью, мы уже можем использовать его сегодня . Firefox уже реализовал некоторые поддержки ECMAScript 6, включая эту область видимости блока. С Chrome вы можете включить экспериментальные функции V8 , переключая переключатель через chrome://flags.

Note: with V8, at least for the time being, you can use let with strict mode only, otherwise it will complain SyntaxError: Illegal let declaration outside extended mode.

What about other browsers? Until they start supporting this block scope feature, you need to fall back to the solution of converting the code (also known as transpiling) into some construct which can be executed by today’s browsers. Using a generic transpiler such as Google Traceur is often the recommended way.

An alternative solution is by using defs.js from Olov Lassus. The idea is to transform block scoped declarations into normal variable statements, obviously taking into account the scope of each declaration. Given the code, defs.js will use Esprima to parse the code, walk the syntax tree, and apply the transformation whenever necessary. As an example, this code fragment:

function f() {
  let j = data.length;
  console.log(j, 'items');
  for (let i = 0; i < j; ++i) {
    let j = data[i] * data[i];
    console.log(j); // squares
  }
}

will be transformed into:

function f() {
  var j = data.length;
  console.log(j, 'items');
  for (var i = 0; i < j; ++i) {
    var j$0 = data[i] * data[i];
    console.log(j$0); // squares
  }
}

Look how defs.js recognizes the right scope for j and therefore masquerade the innermost j with another name, j$0. The transformation itself is non-destructive, defs.js does not bother with anything other than let and const declaration. You can see how a comment is left untouched and the coding style is still exactly the same.

Obviously, there’s much more to defs.js than my simple example above, refer to Olov’s blog post for more details.

How do you plan to use let and const?