В чем разница между использованием "let" и "var"? - PullRequest
3889 голосов
/ 18 апреля 2009

ECMAScript 6 представил оператор let .

Я слышал, что она описывается как "локальная" переменная, но я все еще не совсем уверен, как она ведет себя иначе, чем ключевое слово var.

В чем различия? Когда следует использовать let поверх var?

Ответы [ 32 ]

5303 голосов
/ 12 июля 2012

Разница в объеме. var ограничивается ближайшим функциональным блоком, а let - ближайшим , содержащим блок, который может быть меньше функционального блока. Оба являются глобальными, если находятся вне какого-либо блока.

Кроме того, переменные, объявленные с помощью let, недоступны до того, как объявлены в включающем их блоке. Как видно из демонстрации, это вызовет исключение ReferenceError.

Демо :

var html = '';

write('#### global ####\n');
write('globalVar: ' + globalVar); //undefined, but visible

try {
  write('globalLet: ' + globalLet); //undefined, *not* visible
} catch (exception) {
  write('globalLet: exception');
}

write('\nset variables');

var globalVar = 'globalVar';
let globalLet = 'globalLet';

write('\nglobalVar: ' + globalVar);
write('globalLet: ' + globalLet);

function functionScoped() {
  write('\n#### function ####');
  write('\nfunctionVar: ' + functionVar); //undefined, but visible

  try {
    write('functionLet: ' + functionLet); //undefined, *not* visible
  } catch (exception) {
    write('functionLet: exception');
  }

  write('\nset variables');

  var functionVar = 'functionVar';
  let functionLet = 'functionLet';

  write('\nfunctionVar: ' + functionVar);
  write('functionLet: ' + functionLet);
}

function blockScoped() {
  write('\n#### block ####');
  write('\nblockVar: ' + blockVar); //undefined, but visible

  try {
    write('blockLet: ' + blockLet); //undefined, *not* visible
  } catch (exception) {
    write('blockLet: exception');
  }

  for (var blockVar = 'blockVar', blockIndex = 0; blockIndex < 1; blockIndex++) {
    write('\nblockVar: ' + blockVar); // visible here and whole function
  };

  for (let blockLet = 'blockLet', letIndex = 0; letIndex < 1; letIndex++) {
    write('blockLet: ' + blockLet); // visible only here
  };

  write('\nblockVar: ' + blockVar);

  try {
    write('blockLet: ' + blockLet); //undefined, *not* visible
  } catch (exception) {
    write('blockLet: exception');
  }
}

function write(line) {
  html += (line ? line : '') + '<br />';
}

functionScoped();
blockScoped();

document.getElementById('results').innerHTML = html;

Global:

Они очень похожи при использовании таким образом вне функционального блока.

let me = 'go';  // globally scoped
var i = 'able'; // globally scoped

Однако глобальные переменные, определенные с помощью let, не будут добавлены в качестве свойств глобального объекта window, как те, которые определены с var.

console.log(window.me); // undefined
console.log(window.i); // 'able'

Функция:

Они идентичны при использовании таким образом в функциональном блоке.

function ingWithinEstablishedParameters() {
    let terOfRecommendation = 'awesome worker!'; //function block scoped
    var sityCheerleading = 'go!'; //function block scoped
}

Блок:

Вот разница. let виден только в цикле for(), а var виден всей функции.

function allyIlliterate() {
    //tuce is *not* visible out here

    for( let tuce = 0; tuce < 5; tuce++ ) {
        //tuce is only visible in here (and in the for() parentheses)
        //and there is a separate tuce variable for each iteration of the loop
    }

    //tuce is *not* visible out here
}

function byE40() {
    //nish *is* visible out here

    for( var nish = 0; nish < 5; nish++ ) {
        //nish is visible to the whole function
    }

    //nish *is* visible out here
}

переопределение:

В строгом режиме var позволит вам повторно объявить одну и ту же переменную в той же области видимости. С другой стороны, let не будет:

'use strict';
let me = 'foo';
let me = 'bar'; // SyntaxError: Identifier 'me' has already been declared
'use strict';
var me = 'foo';
var me = 'bar'; // No problem, `me` is replaced.
527 голосов
/ 27 мая 2015

let также можно использовать, чтобы избежать проблем с замыканиями. Он связывает новое значение, а не сохраняет старую ссылку, как показано в примерах ниже.

DEMO

for(var i = 1; i < 6; i++) {
  document.getElementById('my-element' + i)
    .addEventListener('click', function() { alert(i) })
}

Код выше демонстрирует классическую проблему закрытия JavaScript. Ссылка на переменную i хранится в закрытии обработчика щелчка, а не фактическое значение i.

Каждый обработчик одного клика будет ссылаться на один и тот же объект, потому что есть только один встречный объект, который содержит 6, поэтому вы получаете шесть за каждый клик.

Обходной путь - обернуть это в анонимную функцию и передать i в качестве аргумента. Таких проблем можно также избежать, используя let вместо var, как показано в коде ниже.

DEMO (протестировано в Chrome и Firefox 50)

'use strict';

for(let i = 1; i < 6; i++) {
  document.getElementById('my-element' + i)
    .addEventListener('click', function() { alert(i) })
}
144 голосов
/ 23 февраля 2016

В чем разница между let и var?

  • Переменная, определенная с помощью оператора var, известна повсюду функция , в которой она определена, с самого начала функции. (*)
  • Переменная, определенная с помощью оператора let, известна только в блоке , в котором она определена, с момента ее определения и далее. (**)

Чтобы понять разницу, рассмотрим следующий код:

// i IS NOT known here
// j IS NOT known here
// k IS known here, but undefined
// l IS NOT known here

function loop(arr) {
    // i IS known here, but undefined
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( var i = 0; i < arr.length; i++ ) {
        // i IS known here, and has a value
        // j IS NOT known here
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( let j = 0; j < arr.length; j++ ) {
        // i IS known here, and has a value
        // j IS known here, and has a value
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here
}

loop([1,2,3,4]);

for( var k = 0; k < arr.length; k++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS NOT known here
};

for( let l = 0; l < arr.length; l++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS known here, and has a value
};

loop([1,2,3,4]);

// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here

Здесь мы видим, что наша переменная j известна только в первом цикле for, но не до и после. Тем не менее, наша переменная i известна во всей функции.

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


Безопасно ли использовать let сегодня?

Некоторые люди утверждают, что в будущем мы будем использовать ТОЛЬКО операторы let, и что операторы var станут устаревшими. Гуру JavaScript Кайл Симпсон написал очень сложную статью о том, почему он считает, что это не так .

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

  • Если вы пишете код JavaScript на стороне сервера ( Node.js ), вы можете смело использовать оператор let.

  • Если вы пишете код JavaScript на стороне клиента и используете браузер на основе транспилятора (например, Traceur или babel-standalone ), вы можете смело использовать оператор let, однако ваш код может быть не оптимальным с точки зрения производительности.

  • Если вы пишете код JavaScript на стороне клиента и используете транспордер на основе Node (например, сценарий оболочки traceur или Babel ), вы можете смело использовать оператор let. А поскольку ваш браузер будет знать только о переданном коде, недостатки производительности должны быть ограничены.

  • Если вы пишете код JavaScript на стороне клиента и не используете транспортер, вам следует подумать о поддержке браузера.

    Есть еще некоторые браузеры, которые вообще не поддерживают let:

enter image description here


Как отслеживать поддержку браузера

Обновленный обзор того, какие браузеры поддерживают оператор let на момент прочтения этого ответа, см. на этой Can I Use странице .


(*) Глобальные и функциональные переменные могут быть инициализированы и использованы до их объявления, поскольку переменные JavaScript hoisted . Это означает, что объявления всегда много к вершине области.

(**) Переменные области видимости не отображаются

139 голосов
/ 18 апреля 2009

Вот объяснение ключевого слова let с некоторыми примерами.

пусть работает очень похоже на вар. Основное отличие состоит в том, что областью действия переменной var является вся включающая функция

Эта таблица в Википедии показывает, какие браузеры поддерживают Javascript 1.7.

Обратите внимание, что только браузеры Mozilla и Chrome поддерживают его. IE, Safari и, возможно, другие не делают.

105 голосов
/ 02 июня 2015

В принятом ответе отсутствует точка:

{
  let a = 123;
};

console.log(a); // ReferenceError: a is not defined
63 голосов
/ 24 ноября 2016

let

Объем блока

Переменные, объявленные с использованием ключевого слова let, имеют область видимости, то есть они доступны только в блоке , в котором они были объявлены.

На верхнем уровне (вне функции)

На верхнем уровне переменные, объявленные с использованием let, не создают свойств для глобального объекта.

var globalVariable = 42;
let blockScopedVariable = 43;

console.log(globalVariable); // 42
console.log(blockScopedVariable); // 43

console.log(this.globalVariable); // 42
console.log(this.blockScopedVariable); // undefined

Внутри функции

Внутри функции (но за пределами блока) let имеет ту же область действия, что и var.

(() => {
  var functionScopedVariable = 42;
  let blockScopedVariable = 43;

  console.log(functionScopedVariable); // 42
  console.log(blockScopedVariable); // 43
})();

console.log(functionScopedVariable); // ReferenceError: functionScopedVariable is not defined
console.log(blockScopedVariable); // ReferenceError: blockScopedVariable is not defined

Внутри блока

Переменные, объявленные с использованием let внутри блока, недоступны вне этого блока.

{
  var globalVariable = 42;
  let blockScopedVariable = 43;
  console.log(globalVariable); // 42
  console.log(blockScopedVariable); // 43
}

console.log(globalVariable); // 42
console.log(blockScopedVariable); // ReferenceError: blockScopedVariable is not defined

Внутри петли

На переменные, объявленные в циклах let, можно ссылаться только внутри этого цикла.

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

for (let k = 0; k < 3; k++) {
  let l = k * 2;
}
console.log(typeof k); // undefined
console.log(typeof l); // undefined
// Trying to do console.log(k) or console.log(l) here would throw a ReferenceError.

Петли с пробками

Если вы используете let вместо var в цикле, с каждой итерацией вы получаете новую переменную. Это означает, что вы можете безопасно использовать замыкание внутри петли.

// Logs 3 thrice, not what we meant.
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}

// Logs 0, 1 and 2, as expected.
for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log(j), 0);
}

Временная мертвая зона

Из-за временной мертвой зоны переменные, объявленные с использованием let, не могут быть доступны до их объявления. Попытка сделать это выдает ошибку.

console.log(noTDZ); // undefined
var noTDZ = 43;
console.log(hasTDZ); // ReferenceError: hasTDZ is not defined
let hasTDZ = 42;

Без повторного объявления

Нельзя объявлять одну и ту же переменную несколько раз, используя let. Вы также не можете объявить переменную, используя let с тем же идентификатором, что и другая переменная, которая была объявлена ​​с использованием var.

var a;
var a; // Works fine.

let b;
let b; // SyntaxError: Identifier 'b' has already been declared

var c;
let c; // SyntaxError: Identifier 'c' has already been declared

const

const очень похож на let - это блок-область и имеет TDZ. Однако есть две разные вещи.

Нет переназначения

Переменная, объявленная с использованием const, не может быть переназначена.

const a = 42;
a = 43; // TypeError: Assignment to constant variable.

Обратите внимание, что это не означает, что значение является неизменным. Его свойства все еще могут быть изменены.

const obj = {};
obj.a = 42;
console.log(obj.a); // 42

Если вы хотите иметь неизменный объект, вы должны использовать Object.freeze().

Требуется инициализатор

Вы всегда должны указывать значение при объявлении переменной, используя const.

const a; // SyntaxError: Missing initializer in const declaration
44 голосов
/ 06 марта 2015

Вот пример различия между этими двумя (поддержка только что запущена для Chrome):
enter image description here

Как вы можете видеть, переменная var j все еще имеет значение вне области цикла for (Block Scope), но переменная let i не определена вне области цикла for.

"use strict";
console.log("var:");
for (var j = 0; j < 2; j++) {
  console.log(j);
}

console.log(j);

console.log("let:");
for (let i = 0; i < 2; i++) {
  console.log(i);
}

console.log(i);
44 голосов
/ 18 апреля 2009

Есть некоторые тонкие различия - let область видимости ведет себя больше как переменная область видимости в более или менее любых других языках.

например. Он распространяется на вмещающий блок, они не существуют до того, как объявлены, и т. Д.

Однако стоит отметить, что let является лишь частью более новых реализаций Javascript и имеет различную степень поддержки браузера .

21 голосов
/ 22 марта 2017

Основным отличием является разница scope , тогда как let может быть доступна только внутри объявленной области * , как в цикле for, var Например, можно получить вне цикла. Из документации в MDN (примеры также из MDN):

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

Переменные, объявленные let , имеют в качестве области действия блок, в котором они определены, а также любые содержащиеся в нем подблоки. Таким образом, let работает очень похоже на var . Основное отличие состоит в том, что область действия переменной var является всей включающей функцией:

function varTest() {
  var x = 1;
  if (true) {
    var x = 2;  // same variable!
    console.log(x);  // 2
  }
  console.log(x);  // 2
}

function letTest() {
  let x = 1;
  if (true) {
    let x = 2;  // different variable
    console.log(x);  // 2
  }
  console.log(x);  // 1
}`

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

var x = 'global';
let y = 'global';
console.log(this.x); // "global"
console.log(this.y); // undefined

При использовании внутри блока let ограничивает область действия переменной этим блоком. Обратите внимание на разницу между var , область действия которой находится внутри функции, в которой она объявлена.

var a = 1;
var b = 2;

if (a === 1) {
  var a = 11; // the scope is global
  let b = 22; // the scope is inside the if-block

  console.log(a);  // 11
  console.log(b);  // 22
} 

console.log(a); // 11
console.log(b); // 2

Также не забывайте, что это функция ECMA6, так что она еще не полностью поддерживается, поэтому лучше всегда переносить ее в ECMA5, используя Babel и т. Д. ... для получения дополнительной информации о посещении babel website

21 голосов
/ 17 января 2016
  • Переменная не поднимается

    let не будет не поднимать на весь объем блока, в котором они появляются. Напротив, var может подниматься, как показано ниже.

    {
       console.log(cc); // undefined. Caused by hoisting
       var cc = 23;
    }
    
    {
       console.log(bb); // ReferenceError: bb is not defined
       let bb = 23;
    }
    

    На самом деле, Per @Bergi, Оба var и let подняты .

  • Сборка мусора

    Область применения блока let полезна в связи с замыканиями и сборкой мусора для восстановления памяти. Рассмотрим,

    function process(data) {
        //...
    }
    
    var hugeData = { .. };
    
    process(hugeData);
    
    var btn = document.getElementById("mybutton");
    btn.addEventListener( "click", function click(evt){
        //....
    });
    

    Обратному вызову обработчика click вообще не нужна переменная hugeData. Теоретически, после выполнения process(..) огромная структура данных hugeData может быть собрана сборщиком мусора. Тем не менее, возможно, что какой-то движок JS все еще должен будет сохранять эту огромную структуру, поскольку функция click закрывает всю область видимости.

    Тем не менее, область действия блока может превратить эту огромную структуру данных в сборщик мусора.

    function process(data) {
        //...
    }
    
    { // anything declared inside this block can be garbage collected
        let hugeData = { .. };
        process(hugeData);
    }
    
    var btn = document.getElementById("mybutton");
    btn.addEventListener( "click", function click(evt){
        //....
    });
    
  • let петли

    let в цикле может повторно привязывать его к каждой итерации цикла, убедившись, что ему присвоено значение с конца предыдущей итерации цикла. Рассмотрим,

    // print '5' 5 times
    for (var i = 0; i < 5; ++i) {
        setTimeout(function () {
            console.log(i);
        }, 1000);  
    }
    

    Однако замените var на let

    // print 1, 2, 3, 4, 5. now
    for (let i = 0; i < 5; ++i) {
        setTimeout(function () {
            console.log(i);
        }, 1000);  
    }
    

    Поскольку let создает новую лексическую среду с этими именами для а) выражения инициализатора б) каждой итерации (прежде всего для вычисления выражения приращения), более подробная информация здесь .

...