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


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

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


3137


источник


Ответы:


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

4427



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

437



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

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

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

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


127



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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

enter image description here


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

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


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

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


111



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

{
  let a = 123;
};

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

97



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

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

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


39



Here is an example for the difference between the two (support just started for chrome): enter image description here

As you can see the var j variable is still having a value outside of the for loop scope (Block Scope), but the let i variable is undefined outside of the for loop scope.

"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);


39



let

Block scope

Variables declared using the let keyword are block-scoped, which means that they are available only in the block in which they were declared.

At the top level (outside a function)

At the top level, variables declared using let don't create properties on the global object.

var globalVariable = 42;
let blockScopedVariable = 43;

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

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

Inside a function

Inside a funciton (but outside a block), let has the same scope as 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

Inside a block

Variables declared using let inside a block can't be accessed outside that block.

{
  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

Inside a loop

Variables declared with let in loops can be referenced only inside that loop.

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.

Loops with closures

If you use let instead of var in a loop, with each iteration you get a new variable. That means that you can safely use a closure inside a loop.

// 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);
}

Temporal dead zone

Because of the temporal dead zone, variables declared using let can't be accessed before they are declared. Attempting to do so throws an error.

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

No re-declaring

You can't declare the same variable multiple times using let. You also can't declare a variable using let with the same identifier as another variable which was declared using 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 is quite similar to let—it's block-scoped and has TDZ. There are, however, two things which are different.

No re-assigning

Variable declared using const can't be re-assigned.

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

Note that it doesn't mean that the value is immutable. Its properties still can be changed.

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

If you want to have an immutable object, you should use Object.freeze().

Initializer is required

You always must specify a value when declaring a variable using const.

const a; // SyntaxError: Missing initializer in const declaration

36



  • Variable Not Hoisting

    let will not hoist to the entire scope of the block they appear in. By contrast, var could hoist as below.

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

    Actually, Per @Bergi, Both var and let are hoisted.

  • Garbage Collection

    Block scope of let is useful relates to closures and garbage collection to reclaim memory. Consider,

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

    The click handler callback does not need the hugeData variable at all. Theoretically, after process(..) runs, the huge data structure hugeData could be garbage collected. However, it's possible that some JS engine will still have to keep this huge structure, since the click function has a closure over the entire scope.

    However, the block scope can make this huge data structure to garbage collected.

    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 loops

    let in the loop can re-binds it to each iteration of the loop, making sure to re-assign it the value from the end of the previous loop iteration. Consider,

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

    However, replace var with let

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

    Because let create a new lexical environment with those names for a) the initialiser expression b) each iteration (previosly to evaluating the increment expression), more details are here.


20



The main difference is the scope difference, while let can be only available inside the scope it's declared, like in for loop, var can be accessed outside the loop for example. From the documentation in MDN (examples also from MDN):

let allows you to declare variables that are limited in scope to the block, statement, or expression on which it is used. This is unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope.

Variables declared by let have as their scope the block in which they are defined, as well as in any contained sub-blocks. In this way, let works very much like var. The main difference is that the scope of a var variable is the entire enclosing function:

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

At the top level of programs and functions, let, unlike var, does not create a property on the global object. For example:

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

When used inside a block, let limits the variable's scope to that block. Note the difference between var whose scope is inside the function where it is declared.

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

Also don't forget it's ECMA6 feature, so it's not fully supported yet, so it's better always transpiles it to ECMA5 using Babel etc... for more info about visit babel website


15



Here's an example to add on to what others have already written. Suppose you want to make an array of functions, adderFunctions, where each function takes a single Number argument and returns the sum of the argument and the function's index in the array. Trying to generate adderFunctions with a loop using the var keyword won't work the way someone might naïvely expect:

// 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

The process above doesn't generate the desired array of functions because i's scope extends beyond the iteration of the for block in which each function was created. Instead, at the end of the loop, the i in each function's closure refers to i's value at the end of the loop (1000) for every anonymous function in adderFunctions. This isn't what we wanted at all: we now have an array of 1000 different functions in memory with exactly the same behavior. And if we subsequently update the value of i, the mutation will affect all the adderFunctions.

However, we can try again using the let keyword:

// 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

This time, i is rebound on each iteration of the for loop. Each function now keeps the value of i at the time of the function's creation, and adderFunctions behaves as expected.

Now, image mixing the two behaviors and you'll probably see why it's not recommended to mix the newer let and const with the older var in the same script. Doing so can result is some spectacularly confusing code.

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

Don't let this happen to you. Use a linter.

NOTE: This is a teaching example intended to demonstrate the var/let behavior in loops and with function closures that would also be easy to understand. This would be a terrible way to add numbers. But the general technique of capturing data in anonymous function closures might be encountered in the real world in other contexts. YMMV.


12