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

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

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

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

Ответы [ 32 ]

15 голосов
/ 18 августа 2014

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

13 голосов
/ 22 мая 2017

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

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

  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 голосов
/ 08 июля 2016

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

11 голосов
/ 09 сентября 2018

Область действия функции 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 вызывается, переменная bar, объявленная с 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 do будут подняты:

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

11 голосов
/ 17 декабря 2015

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

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
}
7 голосов
/ 11 августа 2015

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

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

var x = 1;
var x = 2;

Это будет:

let x = 1;
let x = 2;
6 голосов
/ 14 октября 2016

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

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

6 голосов
/ 22 мая 2018

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

6 голосов
/ 28 октября 2017

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
3 голосов
/ 18 апреля 2019

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

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