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

3799

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

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

  • 94
    ECMAScript - это стандарт, и let он включен в черновик 6-го издания и, скорее всего, будет в окончательной спецификации.
  • 5
    Смотрите kangax.github.io/es5-compat-table/es6 для обновленной матрицы поддержки функций ES6 (включая let). На момент написания Firefox, Chrome и IE11 все это поддерживали (хотя я считаю, что реализация FF не совсем стандартна).
Показать ещё 7 комментариев
Теги:
ecmascript-6
scope
var
let

31 ответ

5253
Лучший ответ

Разница заключается в определении области. 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;
<pre id="results"></pre>

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.
  • 12
    Помните, что вы можете создать блок, когда захотите. function () {code; {let inBlock = 5; } код; };
  • 129
    Так является ли целью операторы let только для освобождения памяти, когда она не нужна в определенном блоке?
Показать ещё 18 комментариев
518

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) })
}
  • 42
    Это на самом деле круто. Я ожидал бы, что «i» будет определено вне тела цикла, заключенного в скобки, и НЕ будет образовывать «замыкание» вокруг «i». Конечно, ваш пример доказывает обратное. Я думаю, что это немного запутанно с точки зрения синтаксиса, но этот сценарий настолько распространен, что имеет смысл поддерживать его таким образом. Большое спасибо за то, что подняли это.
  • 8
    IE 11 поддерживает let , но предупреждает «6» для всех кнопок. Есть ли у вас источник, говорящий, как let должен себя вести?
Показать ещё 9 комментариев
135

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

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

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

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

  • 5
    Ключевой фрагмент текста из связанного документа выглядит так: «Пусть работает очень похоже на var. Основное отличие состоит в том, что областью действия переменной var является вся включающая функция».
  • 49
    Хотя технически правильно сказать, что IE не поддерживает его, правильнее будет сказать, что это расширение только для Mozilla.
Показать ещё 4 комментария
127

Какая разница между 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 известна только в первом цикле, но не раньше и не позже. Но наша переменная i известна во всей функции.

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


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

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

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

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

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

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

Сегодня, 8 июня 2018 года, есть еще некоторые браузеры, которые не поддерживают let !

Изображение 2012


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

Для актуального обзора того, какие браузеры поддерживают оператор let во время вашего чтения этого ответа, см. Эту страницу " Can я Use.


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

(**) Заблокированные переменные диапазона не поднимаются

  • 13
    относительно ответа v4: i известен везде в функциональном блоке! Он начинается как undefined (из-за подъема), пока вы не назначите значение! ps: let также поднимается (до верхней части содержащего его блока), но выдаст ReferenceError когда на него ссылаются в блоке перед первым присваиванием. (ps2: я парень вроде точки с запятой, но после блока вам не нужна точка с запятой). Тем не менее, спасибо за добавление проверки реальности в отношении поддержки!
  • 0
    @GitaarLAB: Согласно сети разработчиков Mozilla : «В ECMAScript 2015 привязки let не подлежат перемещению переменных, что означает, что объявления let не перемещаются в верхнюю часть текущего контекста выполнения». - Во всяком случае, я сделал несколько улучшений в своем ответе, которые должны прояснить разницу в поведении подъема между let и var !
Показать ещё 3 комментария
98

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

{
  let a = 123;
};

console.log(a); // ReferenceError: a is not defined
  • 59
    Принятый ответ объясняет этот момент.
  • 16
    Принятый ответ НЕ объясняет этот момент в своем примере. Принятый ответ только продемонстрировал это в инициализаторе цикла for , резко сузив сферу применения ограничений let . Upvoted.
Показать ещё 3 комментария
57

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 in, могут ссылаться только внутри этого цикла.

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 -it в блочном пространстве и имеет 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

Вот пример разницы между двумя (поддержка только что началась для chrome): Изображение 2013

Как вы можете видеть, переменная var j по-прежнему имеет значение вне области цикла for (Block Scope), но переменная let i undefined вне области цикла 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);
  • 2
    Какой инструмент я смотрю здесь?
  • 16
    Chrome Devtools
Показать ещё 1 комментарий
39

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

например. Он привязан к закрывающему блоку. Они не существуют до их объявления и т.д.

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

  • 11
    Стоит также отметить, что ECMAScript - это стандарт, и let он включен в черновик 6-го издания и, скорее всего, будет в окончательной спецификации.
  • 22
    Вот разница 3 года: D
Показать ещё 4 комментария
21
  • Переменная не поднимается

    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 создает новую лексическую среду с этими именами для a) выражение инициализатора; b) каждая итерация (предшествующая оценке выражения инкремента), более подробная информация здесь.

  • 1
    На самом деле они все еще подняты
  • 4
    Да, они подняты, но ведут себя как будто не подняты из-за (барабанной дроби) временной мертвой зоны - очень драматичное имя для идентификатора, недоступного до тех пор, пока он не объявлен :-)
Показать ещё 5 комментариев
18

Основное различие заключается разница объема, в то время как пусть может быть доступны только в области видимости она объявлена, как и в течение цикла, вар может быть доступен за пределами цикла, например. Из документации в MDN (примеры также из MDN):

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

Переменные, объявленные с помощью 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
}'

На верхнем уровне программ и функций пусть, в отличие от 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

13

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

// An array of adder functions.
var adderFunctions = [];

for (var i = 0; i < 1000; i++) {
  // We want the function at index i to add the index to its argument.
  adderFunctions[i] = function(x) {
    // What is i bound to here?
    return x + i;
  };
}

var add12 = adderFunctions[12];

// Uh oh. The function is bound to i in the outer scope, which is currently 1000.
console.log(add12(8) === 20); // => false
console.log(add12(8) === 1008); // => true
console.log(i); // => 1000

// It gets worse.
i = -8;
console.log(add12(8) === 0); // => true

Вышеописанный процесс не генерирует желаемый массив функций, поскольку область i выходит за пределы итерации блока for, в котором каждая функция была создана. Вместо этого в конце цикла i в каждом закрытии функции ссылается на значение i в конце цикла (1000) для каждой анонимной функции в adderFunctions. Это совсем не то, чего мы хотели: теперь у нас есть массив из 1000 различных функций в памяти с точно таким же поведением. И если мы впоследствии обновим значение i, мутация повлияет на все adderFunctions.

Однако мы можем попробовать снова с помощью ключевого слова let:

// Let try this again.
// NOTE: We're using another ES6 keyword, const, for values that won't
// be reassigned. const and let have similar scoping behavior.
const adderFunctions = [];

for (let i = 0; i < 1000; i++) {
  // NOTE: We're using the newer arrow function syntax this time, but 
  // using the "function(x) { ..." syntax from the previous example 
  // here would not change the behavior shown.
  adderFunctions[i] = x => x + i;
}

const add12 = adderFunctions[12];

// Yay! The behavior is as expected. 
console.log(add12(8) === 20); // => true

// i scope doesn't extend outside the for loop.
console.log(i); // => ReferenceError: i is not defined

На этот раз i восстанавливается на каждой итерации цикла for. Каждая функция теперь сохраняет значение i во время создания функции, а adderFunctions ведет себя как ожидалось.

Теперь изображение смешивает два поведения, и вы, вероятно, увидите, почему не рекомендуется смешивать новые let и const со старыми var в том же script. Это может привести к некоторому эффектно запутанному коду.

const doubleAdderFunctions = [];

for (var i = 0; i < 1000; i++) {
    const j = i;
    doubleAdderFunctions[i] = x => x + i + j;
}

const add18 = doubleAdderFunctions[9];
const add24 = doubleAdderFunctions[12];

// It not fun debugging situations like this, especially when the
// code is more complex than in this example.
console.log(add18(24) === 42); // => false
console.log(add24(18) === 42); // => false
console.log(add18(24) === add24(18)); // => false
console.log(add18(24) === 2018); // => false
console.log(add24(18) === 2018); // => false
console.log(add18(24) === 1033); // => true
console.log(add24(18) === 1030); // => true

Не позволяйте этому случиться с вами. Используйте linter.

ПРИМЕЧАНИЕ.. Это пример обучения, призванный продемонстрировать поведение var/let в циклах и закрытие функций, которые также будут легко понятны. Это был бы ужасный способ добавить числа. Но общая техника захвата данных при закрытии анонимных функций может встречаться в реальном мире в других контекстах. YMMV.

  • 2
    @aborz: также очень классный синтаксис анонимной функции во втором примере. Это то, к чему я привык в C #. Я кое-что узнал сегодня.
  • 0
    Исправление: Технически, синтаксис функции Arrow описан здесь => developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Показать ещё 1 комментарий
11

Разница заключается в scope переменных, объявленных с каждым.

На практике существует ряд полезных последствий разницы в области:

  • let переменные видны только в ближайшем закрывающем блоке ({ ... }).
  • let переменные используются только в строках кода, которые появляются после объявления переменной (даже если они подняты!).
  • let переменные не могут быть переопределены с помощью следующих var или let.
  • Глобальные переменные let не добавляются к глобальному объекту window.
  • let переменные просты в использовании с закрытием (они не вызывают условия гонки).

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

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

var может по-прежнему быть полезным, если вы уверены, что хотите использовать односвязную функцию при использовании замыкания в цикле (# 5) или для объявления внешне видимых глобальных переменных в вашем коде (# 4). Использование var для экспорта может быть вытеснено, если export мигрирует из пространства транспилера и на основной язык.

Примеры

1. Не использовать внешний ближайший закрывающий блок: Этот блок кода выдает опорную ошибку, потому что второе использование x происходит за пределами блока, где объявлено с помощью let:

{
    let x = 1;
}
console.log(`x is ${x}`);  // ReferenceError during parsing: "x is not defined".

Напротив, работает тот же пример с var.

2. Не использовать до объявления:
Этот блок кода выдаст ReferenceError до того, как код будет запущен, поскольку x используется до его объявления:

{
    x = x + 1;  // ReferenceError during parsing: "x is not defined".
    let x;
    console.log(`x is ${x}`);  // Never runs.
}

В отличие от этого, тот же пример с var анализирует и работает без каких-либо исключений.

3. Нет переоформления: Следующий код демонстрирует, что переменная, объявленная с помощью let, не может быть повторно описана позже:

let x = 1;
let x = 2;  // SyntaxError: Identifier 'x' has already been declared

4. Глобалы, не привязанные к window:

var button = "I cause accidents because my name is too common.";
let link = "Though my name is common, I am harder to access from other JS files.";
console.log(link);  // OK
console.log(window.link);  // undefined (GOOD!)
console.log(window.button);  // OK

5. Простое использование с затворами: Переменные, объявленные с помощью var, не работают хорошо с замыканиями внутри циклов. Вот простой цикл, который выводит последовательность значений, которые переменная i имеет в разные моменты времени:

for (let i = 0; i < 5; i++) {
    console.log(`i is ${i}`), 125/*ms*/);
}

В частности, это выдает:

i is 0
i is 1
i is 2
i is 3
i is 4

В JavaScript мы часто используем переменные значительно позже, чем когда они созданы. Когда мы это продемонстрируем, задерживая выход с замыканием, переданным на setTimeout:

for (let i = 0; i < 5; i++) {
    setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
}

... выход остается неизменным, пока мы придерживаемся let. Напротив, если бы мы использовали var i:

for (var i = 0; i < 5; i++) {
    setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
}

... цикл неожиданно выводит "i 5" пять раз:

i is 5
i is 5
i is 5
i is 5
i is 5
  • 5
    № 5 не вызвано состоянием гонки. Используя var вместо let , код эквивалентен: var i = 0; while (i < 5) { doSomethingLater(); i++; } i находится за пределами замыкания, и к тому времени, когда doSomethingLater() выполняется, i уже был увеличен в 5 раз, поэтому на выходе получается значение i is 5 раз. Используя let , переменная i находится внутри замыкания, поэтому каждый асинхронный вызов получает свою собственную копию i вместо использования «глобальной» копии, созданной с помощью var .
  • 0
    @DanielT .: Я не думаю, что преобразование подъема определения переменной из инициализатора цикла что-либо объясняет. Это просто нормальное определение семантики for . Более точное преобразование, хотя и более сложное, является классическим for (var i = 0; i < 5; i++) { (function(j) { setTimeout(_ => console.log( i is $ {j} ), 125/*ms*/); })(i); } который вводит «запись активации функции» для сохранения каждого значения i с именем j внутри функции.
10

let интересен, поскольку он позволяет нам сделать что-то вроде этого:

(() => {
    var count = 0;

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

Это приводит к подсчету [0, 7].

В то время как

(() => {
    var count = 0;

    for (var i = 0; i < 2; ++i) {
        for (var i = 0; i < 2; ++i) {
            for (var i = 0; i < 2; ++i) {
                console.log(count++);
            }
        }
    }
})();

Учитывается только [0, 1].

  • 2
    это первый раз, когда я видел, чтобы кто-то действовал так, как будто бы желательно использовать переменные тени. нет, цель let - не включать теневое копирование
  • 0
    цель? это конструкция, вы можете использовать ее, как вам угодно, один из интересных способов, как это.
10

Могут отображаться следующие две функции:

function varTest() {
    var x = 31;
    if (true) {
        var x = 71;  // Same variable!
        console.log(x);  // 71
    }
    console.log(x);  // 71
}

function letTest() {
    let x = 31;
    if (true) {
        let x = 71;  // Different variable
        console.log(x);  // 71
    }
    console.log(x);  // 31
}
  • 4
    Еще один интересный момент об этом: jsfiddle.net/6rk8wpso
8

Область действия VS-блока:

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

function testVar () {
  if(true) {
    var foo = 'foo';
  }

  console.log(foo);
}

testVar();  
// logs 'foo'


function testLet () {
  if(true) {
    let bar = 'bar';
  }

  console.log(bar);
}

testLet(); 
// reference error
// bar is scoped to the block of the if statement 

переменные с var :

Когда первая функция testVar получает testVar переменную foo, объявленную с помощью var, все еще доступна вне оператора if. Эта переменная foo будет доступна везде в рамках функции testVar.

переменные с let :

Когда вторая функция testLet переменная bar, объявленная с let, доступна только внутри оператора if. Поскольку переменные, объявленные с let являются блочными (где блок является кодом между фигурными скобками, например if{}, for{}, function{}).

let переменные не поднимаются:

Другое различие между var и let - переменные с объявленными, и let их не поднимают. Пример - лучший способ проиллюстрировать это поведение:

переменные с let not let hoisted:

console.log(letVar);

let letVar = 10;
// referenceError, the variable doesn't get hoisted

переменные с var do get hoist:

console.log(varVar);

var varVar = 10;
// logs undefined, the variable gets hoisted

Global let не привязывается к window:

Переменная, объявленная с let в глобальной области (которая является кодом, который не находится в функции), не добавляется как свойство в глобальном объекте window. Например (этот код находится в глобальной области):

var bar = 5;
let foo  = 10;

console.log(bar); // logs 5
console.log(foo); // logs 10

console.log(window.bar);  
// logs 5, variable added to window object

console.log(window.foo);
// logs undefined, variable not added to window object


Когда следует let использовать над var?

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

6

Также представляется, что, по крайней мере, в Visual Studio 2015, TypeScript 1.5, "var" допускает несколько объявлений с одним и тем же именем переменной в блоке, а "let" - нет.

Это не приведет к ошибке компиляции:

var x = 1;
var x = 2;

Это будет:

let x = 1;
let x = 2;
5

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

Я знаю, что не тот ответ, который вы искали, но это, вероятно, самое важное. Если у вас ограниченное развертывание, где вы знаете, что каждый получает 1,7, то вы счастливый парень.

5

var - глобальная переменная (возможность подъема).

let и const - область действия блока.

test.js

{
    let l = 'let';
    const c = 'const';
    var v = 'var';
    v2 = 'var 2';
}

console.log(v, this.v);
console.log(v2, this.v2);
console.log(l); // ReferenceError: l is not defined
console.log(c); // ReferenceError: c is not defined
4

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

var SomeConstructor;

{
    let privateScope = {};

    SomeConstructor = function SomeConstructor () {
        this.someProperty = "foo";
        privateScope.hiddenProperty = "bar";
    }

    SomeConstructor.prototype.showPublic = function () {
        console.log(this.someProperty); // foo
    }

    SomeConstructor.prototype.showPrivate = function () {
        console.log(privateScope.hiddenProperty); // bar
    }

}

var myInstance = new SomeConstructor();

myInstance.showPublic();
myInstance.showPrivate();

console.log(privateScope.hiddenProperty); // error

Смотрите " Эмуляция частных интерфейсов ".

3

При использовании let

Ключевое слово let присоединяет объявление переменной к какому-либо блоку (обычно a { .. }), в котором он содержится. Другими словами, let неявно захватывает любую область блока для объявления переменной.

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

function a(){
    { // this is the Max Scope for let variable
        let x = 12;
    }
    console.log(x);
}
a(); // Uncaught ReferenceError: x is not defined

При использовании var

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

var можно получить доступ к объектам window, потому что они не могут быть доступны по всему миру.

function a(){ // this is the Max Scope for var variable
    { 
        var x = 12;
    }
    console.log(x);
}
a(); // 12

Если вы хотите узнать больше, продолжайте читать ниже

один из самых известных вопросов интервью по области также может быть достаточным для точного использования let и var, как показано ниже:

При использовании let

for (let i = 0; i < 10 ; i++) {
    setTimeout(
        function a() {
            console.log(i); //print 0 to 9, that is literally AWW!!!
        }, 
        100 * i);
}

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

При использовании var

for (var i = 0; i < 10 ; i++) {
    setTimeout(
        function a() {
            console.log(i); //print 10 times 10
        }, 
        100 * i);
}

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

3

Некоторые хаки с let:

1.

    let statistics = [16, 170, 10];
    let [age, height, grade] = statistics;

    console.log(height)

2.

    let x = 120,
    y = 12;
    [x, y] = [y, x];
    console.log(`x: ${x} y: ${y}`);

3.

    let node = {
                   type: "Identifier",
                   name: "foo"
               };

    let { type, name, value } = node;

    console.log(type);      // "Identifier"
    console.log(name);      // "foo"
    console.log(value);     // undefined

    let node = {
        type: "Identifier"
    };

    let { type: localType, name: localName = "bar" } = node;

    console.log(localType);     // "Identifier"
    console.log(localName);     // "bar"

Getter и setter с let:

let jar = {
    numberOfCookies: 10,
    get cookies() {
        return this.numberOfCookies;
    },
    set cookies(value) {
        this.numberOfCookies = value;
    }
};

console.log(jar.cookies)
jar.cookies = 7;

console.log(jar.cookies)
  • 0
    пожалуйста, что это значит let { type, name, value } = node; ? Вы создаете новый объект с 3 свойствами type / name / value и инициализируете их значениями свойств из узла?
  • 0
    В примере 3 вы повторно объявляете узел, который вызывает исключение. Все эти примеры также отлично работают с var .
2

let является частью es6. Эти функции объяснят разницу легким способом.

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
}
1

Как уже упоминалось выше:

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

Example1:

В обоих моих примерах у меня есть функция myfunc. myfunc содержит переменную myvar 10. В моем первом примере я проверяю, равен ли myvar 10 (myvar==10). Если да, я agian объявляю переменную myvar (теперь у меня есть две переменные myvar), используя ключевое слово var и присваиваю ему новое значение (20). В следующей строке я напечатаю его значение на моей консоли. После условного блока я снова распечатываю значение myvar на моей консоли. Если вы посмотрите на выход myfunc, myvar имеет значение равное 20.

Изображение 2014

Пример2: В моем втором примере вместо использования ключевого слова var в моем условном блоке объявляю myvar используя ключевое слово let. Теперь, когда я вызываю myfunc я получаю два разных выхода: myvar=20 и myvar=10.

Таким образом, разница очень проста, т.е. ее объем.

  • 2
    Пожалуйста, не публикуйте картинки кода, это считается плохой практикой в SO, так как он не будет доступен для поиска для будущих пользователей (а также из-за проблем с доступностью). Кроме того, этот ответ не добавляет ничего, к чему другие ответы еще не обращались.
1

В этой статье четко определена разница между var, let и const

const - это сигнал, что идентификатор не будет переназначен.

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

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

https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75#.esmkpbg9b

1

Теперь я думаю, что лучше использовать область видимости переменных для блока операторов с помощью let:

function printnums()
{
    // i is not accessible here
    for(let i = 0; i <10; i+=)
    {
       console.log(i);
    }
    // i is not accessible here

    // j is accessible here
    for(var j = 0; j <10; j++)
    {
       console.log(j);
    }
    // j is accessible here
}

Я думаю, что люди начнут использовать let here after, чтобы они имели аналогичную область видимости в JavaScript, как и другие языки, Java, С# и т.д.

Люди, не имеющие четкого представления о области видимости в JavaScript, используемые для совершения ошибки ранее.

Подъем не поддерживается с помощью let.

При таком подходе ошибки, возникающие в JavaScript, удаляются.

Обратитесь к ES6 In Depth: let и const, чтобы лучше понять его.

1

Раньше в JavaScript были только две области, то есть функциональные и глобальные. С ключевым словом "let" JavaScript теперь ввел переменные block-level.

Чтобы иметь полное понимание ключевого слова 'let', ES6: 'let ключевое слово для объявления переменной в JavaScript поможет.

0

let позволяет вам объявить переменную, ограниченную по объему блоком (локальная переменная). Основное отличие состоит в том, что область действия переменной var - это вся включающая функция. Например, рассмотрим следующий пример использования let vs. var:

if (foo) {
  let foo = 5; // foo has limited scope
  console.log(true);
} else {
  console.log(false)
}

// ReferenceError: foo is not defined

if (foo) {
  var foo = 5;
  console.log(true);
} else {
  console.log(false)
}

// true, foo can be accessed in the global scope
0

ECMAScript 6 добавило еще одно ключевое слово, чтобы объявлять переменные, другие "const", кроме "let".

Основная цель введения "let" и "const" над "var" заключается в том, чтобы обладать областью охвата вместо традиционной лексической области. В этой статье объясняется очень кратковременная разница между "var" и "let" , а также обсуждается обсуждение "const" .

  • 0
    Последняя часть первого предложения не понятна. Ты можешь починить это?
-1

Посмотрите эту ссылку в MDN

let x = 1;

if (x === 1) {
let x = 2;

console.log(x);
// expected output: 2
}

console.log(x);
// expected output: 1
  • 0
    Это также помогло бы объяснить словами то, что делает let , хотя это сделало бы его дубликатом другого ответа.
  • 0
    Он пытается сказать, что let вне if определяет переменную x=1 . Оператор if сработает (так как x===1 is true . Теперь начинается сложная часть, которая также является основной причиной, по которой вам нужно отслеживать let vs var . Внутри if он пытается установить x=2 , который в console.log(x) будет печатать 2, ОДНАКО, x "снаружи" в if все еще имеет значение 1 , поэтому другой console.log(x) дает 1, так как значение x "глобально "по-прежнему 1 Ответ не объясняет этого, поэтому не следует считать хорошим ответом по моему мнению.
-2

полностью объяснил. Переменные ES2015 - Var, Let & Const

-2

Вот хорошая ссылка. Объявления переменной Variables TypeScript: var, let, const → http://www.tutorialsteacher.com/typescript/typescript-variable

Ещё вопросы

Сообщество Overcoder
Наверх
Меню