it-roy-ru.com

В чем разница между использованием "let" и "var" для объявления переменной в JavaScript?

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

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

3680
TM.

Разница в том, 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>

Глобальный:

Они очень похожи, когда используются как это вне функционального блока.

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.
5094
ThinkingStiff

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) })
}
505
Gurpreet Singh

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

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

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

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

138
Ben S

В чем разница между 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 ), вы можете безопасно использовать оператор let, однако ваш код, вероятно, будет не оптимальным с точки зрения производительности.

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

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

 enter image description here


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

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


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

(**) Переменные области видимости не перемещены

135
John Slegers

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

{
  let a = 123;
};

console.log(a); // ReferenceError: a is not defined
104
Lcf.vs

let

Блок область

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

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

На верхнем уровне переменные, объявленные с использованием 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
59
Michał Perłakowski

Вот пример различий между ними (поддержка только началась для 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);

42
vlio20

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

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

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

42
olliej
  • Переменная не поднимается

    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 loop

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

20
zangw

Основным отличием является различие scope, тогда как let может быть доступно только внутри объявленного scope, как в цикле 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

18
Alireza

Вот пример, чтобы добавить к тому, что уже написали другие. Предположим, вы хотите создать массив функций 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's 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's scope doesn't extend outside the for loop.
console.log(i); // => ReferenceError: i is not defined

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

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

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's 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

Не позволяй этому случиться с тобой. Используйте линтер.

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

14
abroz

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

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

  1. Переменные let видны только в их блоке ближайший вмещающий ({ ... }).
  2. Переменные let можно использовать только в строках кода, которые встречаются после переменная объявлена ​​(даже если они подняты !).
  3. Переменные let не могут быть повторно объявлены последующими var или let.
  4. Глобальные переменные let не добавляются в глобальный объект window.
  5. Переменные 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 is 5» пять раз:

i is 5
i is 5
i is 5
i is 5
i is 5
11
mormegil

Пусть следующие две функции показывают разницу:

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
}
10
Abdennour TOUMI

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].

10
Dmitry

Область действия функции VS:

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

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 вызывается, переменная foo, объявленная с помощью var, все еще доступна вне оператора if. Эта переменная foo будет доступна везде в рамках функции testVar.

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

Когда вторая функция testLet вызывается, переменная bar, объявленная с помощью let, становится доступной только внутри оператора if. Поскольку переменные, объявленные с помощью let, являются block scoped (где блок - это код в фигурных скобках, например, if{}, for{}, function{}). 

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

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

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

console.log(letVar);

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

переменные с vardo будут подняты:

console.log(varVar);

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

Глобальная 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 вместо var, когда это возможно, потому что это просто более определенная область. Это уменьшает потенциальные конфликты именования, которые могут возникнуть при работе с большим количеством переменных. var может использоваться, если вы хотите, чтобы глобальная переменная явно была в объекте window (всегда тщательно продумывайте, действительно ли это необходимо). 

8
Willem van der Veen

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

Это не сгенерирует ошибку компиляции:

var x = 1;
var x = 2;

Это будет:

let x = 1;
let x = 2;
6
RDoc

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

5
Moslem Shahsavan

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

Ключевое слово let присоединяет объявление переменной к области действия любого блока (обычно пары { .. }), в котором она содержится. Другими словами, 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 для каждой итерации цикла переменная находится в области видимости и имеет общую копию.

4
Ankur Soni

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

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

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

4
Daniel Sokolowski

Некоторые хаки с 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"

Получатель и установщик с 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)
3
zloctb

пусть является частью 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
}
2
vipul jain

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

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

1
Hitesh Garg

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

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

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

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

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

1
anandharshan

Теперь я думаю, что есть лучшая область видимости переменных для блока операторов, использующих 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 после этого, чтобы у них была такая же область видимости в JavaScript, как и в других языках, Java, C # и т.д.

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

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

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

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

1
swaraj patil

пусть против вар. Это все о сфера .

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

Посмотрите мой пример ниже и обратите внимание, как переменная lion (let) действует по-разному в двух console.logs; это выходит из области видимости во втором console.log.

var cat = "cat";
let dog = "dog";

var animals = () => {
    var giraffe = "giraffe";
    let lion = "lion";

    console.log(cat);  //will print 'cat'.
    console.log(dog);  //will print 'dog', because dog was declared outside this function (like var cat).

    console.log(giraffe); //will print 'giraffe'.
    console.log(lion); //will print 'lion', as lion is within scope.
}

console.log(giraffe); //will print 'giraffe', as giraffe is a global variable (var).
console.log(lion); //will print UNDEFINED, as lion is a 'let' variable and is now out of scope.
1
daCoda

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

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

Example1:

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

 let keyword

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

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

1
N Randhawa

 enter image description here

Взгляните на это изображение, я создал один очень простой пример для демонстрации переменных const и let. Как вы можете видеть, когда вы пытаетесь изменить переменную const, вы получите ошибку (Попытка переопределить 'name', которая является постоянной '), но посмотрите на переменную let ... 

Сначала мы объявляем let age = 33, а затем присваиваем какое-то другое значение age = 34;, что нормально, у нас нет ошибок при попытке изменить переменную let

0
Mile Mijatovic

Я хочу связать эти ключевые слова с контекстом выполнения, потому что контекст выполнения важен во всем этом. Контекст выполнения имеет две фазы: фаза создания и фаза выполнения. Кроме того, каждый контекст выполнения имеет переменную среду и внешнюю среду (свою лексическую среду).

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

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

function a(){
    b;
    let b;
}
a();
> Uncaught ReferenceError: b is not defined

С переменной, определенной с помощью var, если Engine не может найти переменную в переменной среде текущего контекста выполнения, он поднимется по цепочке областей действия (внешняя среда) и проверит переменную среду внешней среды для переменной. Если он не может найти его там, он продолжит поиск в Scope Chain. Это не относится к let и const.

Вторая особенность let - это введение в область видимости блока. Блоки определяются фигурными скобками. Примеры включают в себя функциональные блоки, блоки if, для блоков и т.д. Когда вы объявляете переменную с помощью let inside блока, переменная доступна только внутри блока. Фактически, каждый раз, когда блок запускается, например, в цикле for, он создает новую переменную в памяти.

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

И, наконец, когда дело доходит до контекста выполнения, переменные, определенные с помощью var, будут присоединены к объекту 'this'. В глобальном контексте выполнения это будет объект окна в браузерах. Это не относится к let или const.

0
Donato

Я думаю, что термины и большинство примеров немного ошеломляют. Основная проблема, с которой я столкнулся лично, - это понимание, что такое «Блок». В какой-то момент я понял, что блоком будут любые фигурные скобки, кроме оператора IF. открывающая скобка { функции или цикла определит новый блок, все, что определено с помощью let внутри него, не будет доступно после закрывающей скобки } того же самого элемента (функции или цикла); Имея это в виду, было легче понять:

let msg = "Hello World";

function doWork() { // msg will be available since it was defined above this opening bracket!
  let friends = 0;
  console.log(msg);

  // with VAR though:
  for (var iCount2 = 0; iCount2 < 5; iCount2++) {} // iCount2 will be available after this closing bracket!
  console.log(iCount2);
  
    for (let iCount1 = 0; iCount1 < 5; iCount1++) {} // iCount1 will not be available behind this closing bracket, it will return undefined
  console.log(iCount1);
  
} // friends will no be available after this closing bracket!
doWork();
console.log(friends);
0
Dementic

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

Понимание разницы между var и let может быть проще, если мы понимаем разницу между function и scope block .

Давайте рассмотрим следующие случаи:

(function timer() {
    for(var i = 0; i <= 5; i++) {
        setTimeout(function notime() { console.log(i); }, i * 1000);
    }
})();


   Stack            VariableEnvironment //one VariablEnvironment for timer();
                                       // when the timer is out - the value will be the same value for each call
5. [setTimeout, i]  [i=5] 
4. [setTimeout, i]  
3. [setTimeout, i]
2. [setTimeout, i]
1. [setTimeout, i]
0. [setTimeout, i]

####################    

(function timer() {
    for (let i = 0; i <= 5; i++) {
        setTimeout(function notime() { console.log(i); }, i * 1000);
    }
})();

   Stack           LexicalEnvironment - each iteration has a new lexical environment
5. [setTimeout, i]  [i=5]       
                      LexicalEnvironment 
4. [setTimeout, i]    [i=4]     
                        LexicalEnvironment 
3. [setTimeout, i]      [i=3]       
                         LexicalEnvironment 
2. [setTimeout, i]       [i=2]
                           LexicalEnvironment 
1. [setTimeout, i]         [i=1]
                             LexicalEnvironment 
0. [setTimeout, i]           [i=0]

когда timer() вызывается, то создается ExecutionContext, который будет содержать и VariableEnvironment, и все LexicalEnvironments, соответствующие каждой итерации.

И более простой пример

Область действия функции

function test() {
    for(var z = 0; z < 69; z++) {
        //todo
    }
    //z is visible outside the loop
}

Блок Сфера

function test() {
    for(let z = 0; z < 69; z++) {
        //todo
    }
    //z is not defined :(
}
0
Lucian Nut