[javascript] “let”과“var”의 차이점은 무엇입니까?

ECMAScript 6는 let진술을 소개 했습니다 .

“로컬”변수라고 설명되어 있지만 var키워드 와 어떻게 다르게 동작하는지 잘 모르겠습니다 .

차이점은 무엇입니까? 때해야 let이상 사용할 수 var?



답변

범위 지정 규칙

주요 차이점은 범위 지정 규칙입니다. var키워드로 선언 된 변수는 직접 함수 본문 (따라서 함수 범위) let에 범위가 지정되는 반면 변수는 즉시 둘러싸 는 범위에 있습니다.{ } (따라서 블록 범위) 로 표시되는 블록에 .

function run() {
  var foo = "Foo";
  let bar = "Bar";

  console.log(foo, bar);

  {
    let baz = "Bazz";
    console.log(baz);
  }

  console.log(baz); // ReferenceError
}

run();

let키워드가 언어에 도입 된 이유 는 함수 범위이기 때문에 혼란스럽고 JavaScript의 주요 버그 소스 중 하나였습니다.

다른 stackoverflow 질문 에서이 예제를 살펴보십시오 .

var funcs = [];
// let's create 3 functions
for (var i = 0; i < 3; i++) {
  // and store them in funcs
  funcs[i] = function() {
    // each should log its value.
    console.log("My value: " + i);
  };
}
for (var j = 0; j < 3; j++) {
  // and now let's run each one to see
  funcs[j]();
}

My value: 3funcs[j]();익명 함수가 동일한 변수에 바인드되어 호출 될 때마다 콘솔에 출력되었습니다 .

사람들은 루프에서 올바른 값을 캡처하기 위해 즉시 호출 된 함수를 만들어야했지만 머리카락도 많았습니다.

게양

var키워드로 선언 된 변수 는 호이 스팅 ( undefined코드가 실행되기 전에 초기화 됨 )되지만 선언되기 전에도 포함 범위에서 액세스 할 수 있습니다.

function run() {
  console.log(foo); // undefined
  var foo = "Foo";
  console.log(foo); // Foo
}

run();

let변수는 정의가 평가 될 때까지 초기화되지 않습니다. 초기화하기 전에 액세스하면 ReferenceError. 변수는 블록의 시작부터 초기화가 처리 될 때까지 “임시 데드 존”에 있다고합니다.

function checkHoisting() {
  console.log(foo); // ReferenceError
  let foo = "Foo";
  console.log(foo); // Foo
}

checkHoisting();

전역 객체 속성 생성

최상위 레벨 let에서는와 달리 var전역 객체에 대한 속성을 만들지 않습니다.

var foo = "Foo";  // globally scoped
let bar = "Bar"; // globally scoped

console.log(window.foo); // Foo
console.log(window.bar); // undefined

재 선언

엄격 모드에서는 SyntaxError를 발생 var시키면서 동일한 범위에서 동일한 변수를 다시 선언 할 수 있습니다 let.

'use strict';
var foo = "foo1";
var foo = "foo2"; // No problem, 'foo' is replaced.

let bar = "bar1";
let bar = "bar2"; // SyntaxError: Identifier 'bar' has already been declared


답변

let폐쇄와 관련된 문제를 피하기 위해 사용할 수도 있습니다. 아래 예제와 같이 오래된 참조를 유지하는 대신 새로운 가치를 결속시킵니다.

for(var i=1; i<6; i++) {
  $("#div" + i).click(function () { console.log(i); });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>Clicking on each number will log to console:</p>
<div id="div1">1</div>
<div id="div2">2</div>
<div id="div3">3</div>
<div id="div4">4</div>
<div id="div5">5</div>

위 코드는 전형적인 JavaScript 클로저 문제를 보여줍니다. i변수 에 대한 참조 는의 실제 값이 아닌 클릭 핸들러 클로저에 저장됩니다 i.

모든 단일 클릭 핸들러는 6을 보유하는 카운터 오브젝트가 하나만 있으므로 각 클릭마다 6 개를 가져 오기 때문에 동일한 오브젝트를 참조합니다.

일반적인 해결 방법은 이것을 익명 함수로 감싸서 i인수로 전달 하는 것입니다. 이러한 문제는 다음을 사용하여 피할 수도 있습니다.letvar아래 코드와 같이 대신 .

(Chrome 및 Firefox 50에서 테스트)

for(let i=1; i<6; i++) {
  $("#div" + i).click(function () { console.log(i); });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>Clicking on each number will log to console:</p>
<div id="div1">1</div>
<div id="div2">2</div>
<div id="div3">3</div>
<div id="div4">4</div>
<div id="div5">5</div>


답변

차이 무엇 letvar?

  • 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 전문가 인 Kyle Simpson이것이 사실이 아니라고 믿는 이유에 대해 매우 정교한 기사를 썼습니다 .

그러나 오늘날에는 그렇지 않습니다. 실제로, 우리는 실제로 사용하기에 안전한지 스스로에게 물어볼 필요가 있습니다.let 진술 . 해당 질문에 대한 답변은 환경에 따라 다릅니다.

  • 서버 측 JavaScript 코드 ( Node.js )를 작성하는 경우 안전하게 사용할 수 있습니다.let 작성 명령문을 .

  • 클라이언트 측 JavaScript 코드를 작성하고 Traceur 또는 babel-standalone 과 같은 브라우저 기반 변환기를 사용하는 경우 안전하게 사용할 수 있습니다.let 명령문을 있지만 코드는 성능 측면에서 최적 일 수 있습니다.

  • 클라이언트 측 JavaScript 코드를 작성하고 traceur 쉘 스크립트 또는 Babel 과 같은 노드 기반 변환기를 사용하는 경우 안전하게 사용할 수 있습니다.let 명령문을 . 브라우저는 변환 된 코드에 대해서만 알기 때문에 성능 단점이 제한되어야합니다.

  • 클라이언트 측 JavaScript 코드를 작성 중이고 트랜스 파일러를 사용하지 않는 경우 브라우저 지원을 고려해야합니다.

    전혀 지원하지 않는 브라우저가 여전히 있습니다 let:

여기에 이미지 설명을 입력하십시오


브라우저 지원을 추적하는 방법

let이 답변을 읽을 당시의 진술을 지원하는 브라우저에 대한 최신 개요는 Can I Use페이지를 참조 하십시오 .


(*) JavaScript 변수가 들어 있기 때문에 전역 및 기능적으로 범위가 지정된 변수를 선언하기 전에 초기화하고 사용할 수 있습니다 .이것은 선언이 항상 범위의 맨 위에 있다는 것을 의미합니다.

(**) 블록 범위 변수는 게양되지 않습니다


답변

다음 은 몇 가지 예와 함께 키워드에 대한 설명입니다let .

let매우 유사하게 작동합니다 var. 가장 큰 차이점은 var변수 의 범위 가 전체 둘러싸는 함수라는 것입니다

Wikipedia 의이 표 는 Javascript 1.7을 지원하는 브라우저를 보여줍니다.

Mozilla 및 Chrome 브라우저 만 지원합니다. IE, Safari 및 잠재적으로 다른 사람들은 그렇지 않습니다.


답변

수락 된 답변에 요점이 없습니다.

{
  let a = 123;
};

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


답변

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

루프 내부

letin 루프로 선언 된 변수는 해당 루프 내에서만 참조 할 수 있습니다.

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

일시적 데드 존

일시적인 데드 존 (dead zone)으로 인해 선언 된 변수는 선언 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


답변

다음은이 둘의 차이점에 대한 예입니다 (Chrome에 대한 지원 시작).
여기에 이미지 설명을 입력하십시오

보시다시피 var j변수는 여전히 for 루프 범위 (블록 범위) 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);