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

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

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

45 ответов

Решение

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

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 как показано в коде ниже.

ДЕМО (протестировано в Chrome и Firefox 50)

'use strict';

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

В чем разница между 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!


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

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


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

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

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

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

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

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

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 - имеет ограниченную область видимости и имеет ТДЗ. Однако есть две разные вещи.

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

Переменная, объявленная с использованием 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

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

{
  let a = 123;
};

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

Проще говоря,

for (let i = 0; i < 5; i++) {
  // i accessible ✔️
}
// i not accessible ❌

for (var i = 0; i < 5; i++) {
  // i accessible ✔️
}
// i accessible ✔️

⚡️ Песочница для игры ↓

Редактировать let vs var

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

Как вы можете видеть 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);

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

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

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

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

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

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

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

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

  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

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

Это объяснение было взято из статьи, которую я написал на Medium:

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

       console.log (hi);     
var hi = "say hi";

на самом деле интерпретируется к этому

       var hi = undefined;
console.log (hi);
hi = "say hi";

Итак, как мы только что видели, переменные поднимаются наверх своей области видимости и инициализируются со значением undefined, что означает, что мы можем на самом деле присвоить их значение до фактического объявления их в коде следующим образом:

       hi = “say hi”
console.log (hi); // say hi
var hi; Regarding function declarations, we can invoke them before actually declaring them like so:

sayHi(); // Hi

function sayHi() {
   console.log('Hi');
}; Function expressions, on the other hand, are not hoisted, so we’ll get the following error:

sayHi(); //Output: "TypeError: sayHi is not a function

var sayHi = function() {
  console.log('Hi');
}; ES6 introduced JavaScript developers the `let` and `const` keywords. While `let` and `const` are block-scoped and not function

ограничено, так как это не должно иметь значения при обсуждении их поведения при подъеме. Мы начнем с конца, подъемники JavaScript и .

       console.log(hi); // Output: Cannot access 'hi' before initialization 
let hi = 'Hi';

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

       console.log(hi); // Output: Cannot access 'hi' before initialization
const hi = 'Hi';

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

Я добавил эту наглядную иллюстрацию, чтобы помочь понять, как поднятые переменные и функции сохраняются в памяти.

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

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
}

ES6 представил два новых ключевых слова (let и const), заменяющие var.

Когда вам нужно замедление на уровне блока, вы можете использовать let и const вместо var.

В таблице ниже приведены различия между var, let и const.

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

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

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

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

Еще одно различие между var а также let переменные с объявленным с let не поднимайся Пример - лучший способ проиллюстрировать это поведение:

переменные с let не поднимайся

console.log(letVar);

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

переменные с var получить подъем:

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 объект (всегда тщательно продумывайте, если это действительно необходимо).

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

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

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

var x = 1;
var x = 2;

Это будет:

let x = 1;
let x = 2;
      var   --> Function scope  
let   --> Block scope
const --> Block scope

вар

В этом примере кода переменная объявляется с помощьюvar. Следовательно, у него есть область видимости функции . Это означает, что вы можете получить доступiтолько изнутри. Вы не можете прочитать это снаружиfunction x

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

Если я прочитал спецификации прямо тогда 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

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

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

Некоторые взломы с 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)

Пусть против вар. Все дело в сфере.

Переменные 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.

Ниже показано, как 'let' и 'var' различаются по области действия:

let gfoo = 123;
if (true) {
    let gfoo = 456;
}
console.log(gfoo); // 123

var hfoo = 123;
if (true) {
    var hfoo = 456;
}
console.log(hfoo); // 456

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

В то время как hfoo, определяется varизначально находится в глобальной области видимости, но снова, когда мы объявляем его внутриif clause, он считает глобальную область видимости hfoo, хотя для ее объявления снова использовался var. И когда мы повторно назначаем его значение, мы видим, что глобальная область видимости hfoo также затронута. Это основное отличие.

Я только что наткнулся на один вариант использования, который мне пришлось использовать var над letввести новую переменную. Вот такой случай:

Я хочу создать новую переменную с динамическими именами переменных.

let variableName = 'a';
eval("let " + variableName + '= 10;');
console.log(a);   // this doesn't work
let variableName = 'a';
eval("var " + variableName + '= 10;');
console.log(a);   // this works

Приведенный выше код не работает, потому что evalвводит новый блок кода. Декларация с использованием var объявит переменную вне этого блока кода, поскольку var объявляет переменную в области видимости функции.

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

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

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

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

Example1:

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

пусть ключевое слово

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

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

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

Другие вопросы по тегам