ECMAScript 6는 그 let
진술을 소개 했습니다 .
"로컬"변수라고 설명되어 있지만 var
키워드 와 어떻게 다르게 동작하는지 잘 모르겠습니다 .
차이점은 무엇입니까? 때해야 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: 3
funcs[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
블록 표현식 let (variable declaration) statement
은 표준이 아니며 향후 bugzilla.mozilla.org/show_bug.cgi?id=1023609 에서 제거 될 예정 입니다.
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
인수로 전달 하는 것입니다. 이러한 문제는 다음을 사용하여 피할 수도 있습니다.let
var
아래 코드와 같이 대신 .
(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>
let
하지만 모든 버튼에 대해 "6"을 경고합니다. 어떻게 let
행동 해야하는지 말하는 소스 가 있습니까?
let
실제로 유용한 이유를 알 수 있습니다. 루프에서 이벤트 리스너를 설정하면 더 이상 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 전문가 인 Kyle Simpson 은 이것이 사실이 아니라고 믿는 이유에 대해 매우 정교한 기사를 썼습니다 .
그러나 오늘날에는 그렇지 않습니다. 실제로, 우리는 실제로 사용하기에 안전한지 스스로에게 물어볼 필요가 있습니다.let
진술 . 해당 질문에 대한 답변은 환경에 따라 다릅니다.
서버 측 JavaScript 코드 ( Node.js )를 작성하는 경우 안전하게 사용할 수 있습니다.let
작성 명령문을 .
클라이언트 측 JavaScript 코드를 작성하고 Traceur 또는 babel-standalone 과 같은 브라우저 기반 변환기를 사용하는 경우 안전하게 사용할 수 있습니다.let
명령문을 있지만 코드는 성능 측면에서 최적 일 수 있습니다.
클라이언트 측 JavaScript 코드를 작성하고 traceur 쉘 스크립트 또는 Babel 과 같은 노드 기반 변환기를 사용하는 경우 안전하게 사용할 수 있습니다.let
명령문을 . 브라우저는 변환 된 코드에 대해서만 알기 때문에 성능 단점이 제한되어야합니다.
클라이언트 측 JavaScript 코드를 작성 중이고 트랜스 파일러를 사용하지 않는 경우 브라우저 지원을 고려해야합니다.
전혀 지원하지 않는 브라우저가 여전히 있습니다 let
:
let
이 답변을 읽을 당시의 진술을 지원하는 브라우저에 대한 최신 개요는 이 Can I Use
페이지를 참조 하십시오 .
(*) JavaScript 변수가 들어 있기 때문에 전역 및 기능적으로 범위가 지정된 변수를 선언하기 전에 초기화하고 사용할 수 있습니다 .이것은 선언이 항상 범위의 맨 위에 있다는 것을 의미합니다.
(**) 블록 범위 변수는 게양되지 않습니다
i
함수 블록의 모든 곳에서 알려져 있습니다! undefined
값을 할당 할 때까지 (상승으로 인해) 시작 합니다! ps : let
도 포함되어 있습니다 (블록을 포함하는 맨 위에 있음). 그러나 ReferenceError
처음 할당하기 전에 블록에서 참조 될 때 시간 을 줄 것 입니다. (ps2 : 나는 프로 세미콜론 같은 사람이지만 실제로 블록 뒤에 세미콜론이 필요하지 않습니다). 지원에 관한 현실 점검을 추가해 주셔서 감사합니다!
let
와 사이의 호이 스팅 행동의 차이점을 분명히해야합니다 var
!
let
하고 const
있었다 당신이 실제로 자신의 추가 기능이 필요한 경우에만 사용하는 것이 좋습니다 , '더 많은 작업의 결과 (쓰기 전용 const를 같은) 이러한 추가 기능 / 강제 점검 때문에 (현재) 엔진에 대해 '(및 범위 트리의 추가 범위 노드)를 적용 / 확인 / 확인 / 설정합니다.
다음 은 몇 가지 예와 함께 키워드에 대한 설명입니다let
.
let
매우 유사하게 작동합니다var
. 가장 큰 차이점은var
변수 의 범위 가 전체 둘러싸는 함수라는 것입니다
Wikipedia 의이 표 는 Javascript 1.7을 지원하는 브라우저를 보여줍니다.
Mozilla 및 Chrome 브라우저 만 지원합니다. IE, Safari 및 잠재적으로 다른 사람들은 그렇지 않습니다.
let
msdn.microsoft.com/en-us/library/ie/dn342892%28v=vs.85%29.aspx
수락 된 답변에 요점이 없습니다.
{
let a = 123;
};
console.log(a); // ReferenceError: a is not defined
for
루프 이니셜 라이저 에서만 입증되었으며 의 적용 범위가 크게 좁아 let
졌습니다. 공감.
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);
}
일시적인 데드 존 (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);
약간의 차이점이 있습니다. let
범위 지정은 다른 언어에서 변수 범위 지정과 비슷하게 동작합니다.
예를 들어, 그것은 블록을 둘러싸고, 선언되기 전에 존재하지 않습니다.
그러나 let
최신 자바 스크립트 구현의 일부일 뿐이며 다양한 수준의 브라우저 지원 기능 이 있다는 점 은 주목할 가치가 있습니다.
let
. Safari, IE 및 Chome은 모두 그렇지 않습니다.
let
변수를 사용하는 것이 좋습니다 let
. if
단지 몇 줄의 코드 이상의 문장이 있다면 , 변수가 정의 될 때까지 해당 변수를 사용할 수 없다는 것을 잊을 수 있습니다. 좋은 점 !!!
let
끌어 올립니다 . 그러나 변수 선언 전에 블록에서 변수를 참조 하면 ReferenceError가 발생합니다 (나의 참고 : good old 대신 undefined
). 변수는 블록의 시작부터 선언이 처리 될 때까지 '임시 데드 존'에 있습니다. " "기본 블록이 하나뿐이므로 스위치 문"도 마찬가지입니다. 출처 : developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
주요 차이점은입니다 범위 반면, 차이 하자가 내부에서만 사용할 수 있습니다 범위 , 루프처럼이 선언있어 VAR는 예를 들어 루프 외부에서 액세스 할 수 있습니다. MDN 설명서 (예 : MDN) :
하자가 당신이 사용되는 블록, 문, 또는 표현 범위가 제한되는 변수를 선언 할 수 있습니다. 이것은 블록 범위와 상관없이 변수를 전체적으로 또는 전체적으로 로컬로 정의하는 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
블록 내에서 사용될 때 변수의 범위를 해당 블록으로 제한하십시오. 범위가 선언 된 함수 내에 범위가있는 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 기능을 잊지 마십시오. 아직 완벽하게 지원되지는 않으므로 babel 웹 사이트 방문에 대한 자세한 내용은 Babel 등을 사용하여 ECMA5로 항상 변환하는 것이 좋습니다.
게양하지 않는 변수
let
것이다 하지 호이스트 가 나타나지 블록의 전체 범위이다. 이와 대조적으로, var
아래로 끌어 올릴 수있다.
{
console.log(cc); // undefined. Caused by hoisting
var cc = 23;
}
{
console.log(bb); // ReferenceError: bb is not defined
let bb = 23;
}
사실, 당 @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
가 가비지 수집 될 수 있습니다. 그러나 click
함수가 전체 범위에 걸쳐 폐쇄되어 있기 때문에 일부 JS 엔진은 여전히이 거대한 구조를 유지해야 할 수도 있습니다.
그러나 블록 범위는이 거대한 데이터 구조를 가비지 수집으로 만들 수 있습니다.
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
)) 각각의 반복을 initialiser 표현 B를 A의 그 이름을 가진 새로운 어휘 환경을 조성 (와 previosly 증가 표현)을 평가하는 자세한 내용은 여기 .
다음은 다른 사람들이 이미 작성한 내용을 추가하는 예입니다. 함수의 배열을 만들고 싶다고 가정합니다 adderFunctions
. 여기서 각 함수는 단일 숫자 인수를 가져 와서 인수와 함수의 인덱스의 합계를 배열로 반환합니다. 키워드를 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.
let value = i;
. 이 for
문장은 어휘 블록을 만듭니다.
차이점은 각각 선언 된 변수 의 범위 에 있습니다.
실제로 범위 차이로 인해 여러 가지 유용한 결과가 있습니다.
let
변수는 가장 가까운 둘러싸는 블록 ( { ... }
) 에만 표시됩니다 .let
변수는 변수가 선언 된 후에 발생하는 코드 줄에서만 사용할 수 있습니다 ( 호이 스팅 되더라도) !).let
변수는 이후 var
또는 로 다시 선언 할 수 없습니다 let
.let
변수는 전역 window
개체에 추가되지 않습니다 .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
var
대신에 let
코드 를 사용 하면 코드가 다음과 같습니다. var i = 0; while (i < 5) { doSomethingLater(); i++; }
i
은 폐쇄 외부에 있으며 doSomethingLater()
실행 i
시간에 따라 이미 5 배씩 증가하므로 출력은 i is 5
5 배입니다. 를 사용 let
하면 변수 i
가 클로저 내에 있으므로 각 비동기 호출은로 i
작성된 '전역' 호출 대신 자체 사본을 가져 옵니다 var
.
for
. 좀 더 정확한 변환은 더 복잡하지만 고전적인 for (var i = 0; i < 5; i++) { (function(j) { setTimeout(_ => console.log(
i is $ {j} ), 125/*ms*/); })(i); }
는 "function-activation record"를 도입하여 각 값을 함수 내부의 i
이름으로 저장 j
합니다.
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] 만 계산합니다.
주요 차이점 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
DO GET 게양 :
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
객체 에 배치하려고 할 때 사용할 수 있습니다 (실제로 필요한 경우 항상 신중하게 고려하십시오).
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
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
고맙게도 개인 전용 멤버를 시뮬레이션하는 데 사용되는 자체 호출 기능 을 피할 수 있습니다 - 코드 가독성을 낮추고 디버깅을 복잡하게 만드는 인기있는 디자인 패턴 - 실제 코드 보호 또는 다른 이점을 추가하지 않는 다른 사람을 만족시키는 것을 제외하고- 의미를 원하면 사용을 중지하십시오. / 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
'보기 개인 인터페이스를 에뮬레이션 '
let
? (“자기 호출 기능”을 가진 IIFE를 의미한다고 가정합니다.)
hiddenProperty
생성자에서 설정 합니까? hiddenProperty
“클래스”에는 모든 인스턴스에 대해 하나만 있습니다 .
for (let i = 0; i < 5; i++) {
// i accessible ✔️
}
// i not accessible ❌
for (var i = 0; i < 5; i++) {
// i accessible ✔️
}
// i accessible ✔️
⚡️ 샌드 박스는 ↓
일부 해킹 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}`);
삼.
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)
let { type, name, value } = node;
합니까? 3 가지 속성 유형 / 이름 / 값으로 새 객체를 생성하고 노드의 속성 값으로 초기화합니다.
var
.
let vs var. 범위 에 관한 것 입니다.
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은 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
}
아래는 '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
var에 선언을 다시 사용되었지만, 그것은, 글로벌 범위 hfoo을 고려합니다. 그리고 우리가 그 값을 다시 할당 할 때 우리는 전역 범위 hfoo도 영향을 받는다는 것을 알 수 있습니다. 이것이 주요 차이점입니다.
상술 한 바와 같이:
차이점은 범위입니다.
var
가까운 범위로되는 기능 블록 및let
받는 범위가 가까운 바깥 블록 기능 블록보다 작을 수있다. 블록 외부에있는 경우 둘 다 전역입니다.
예 1 :
내 두 가지 예 모두에 함수가 myfunc
있습니다. myfunc
변수에 포함 myvar
나는 경우 확인 내 첫 번째 예에서 10 등호를 myvar
등호 10 ( myvar==10
). 그렇다면 agian myvar
은 var
키워드를 사용하여 변수를 선언하고 (이제 두 개의 myvar 변수가 있음) 새로운 값을 지정합니다 (20). 다음 줄에서는 콘솔에 값을 인쇄합니다. 조건부 블록 후에 다시 myvar
콘솔에 값을 인쇄합니다 . 당신의 출력을 보면 myfunc
, myvar
값이 20로 동일있다.
예 2 :var
조건부 블록에서 키워드
를 사용하는 대신 두 번째 예에서는 keyword를 myvar
사용하여 선언 let
합니다. 지금은 호출 할 때 myfunc
나는 두 개의 서로 다른 출력을 얻을 : myvar=20
와 myvar=10
.
따라서 그 차이는 매우 간단합니다. 즉 범위입니다.
실행 컨텍스트가이 모든 것에서 중요하기 때문에 이러한 키워드를 실행 컨텍스트에 연결하려고합니다. 실행 컨텍스트에는 작성 단계와 실행 단계의 두 단계가 있습니다. 또한 각 실행 컨텍스트에는 가변 환경 및 외부 환경 (Lexical Environment)이 있습니다.
실행 컨텍스트의 생성 단계 동안 var, let 및 const는 주어진 실행 컨텍스트의 변수 환경에서 정의되지 않은 값으로 변수를 메모리에 저장합니다. 차이점은 실행 단계에 있습니다. var에 정의 된 변수에 값을 할당하기 전에 reference를 사용하면 정의되지 않습니다. 예외는 발생하지 않습니다.
그러나 let 또는 const로 선언 된 변수는 선언 될 때까지 참조 할 수 없습니다. 선언하기 전에 사용하려고하면 실행 컨텍스트의 실행 단계에서 예외가 발생합니다. 이제 변수는 실행 컨텍스트의 작성 단계에 의해 여전히 메모리에 남아 있지만 엔진에서는이를 사용할 수 없습니다.
function a(){
b;
let b;
}
a();
> Uncaught ReferenceError: b is not defined
var로 정의 된 변수를 사용하여 엔진이 현재 실행 컨텍스트의 변수 환경에서 변수를 찾을 수 없으면 범위 체인 (외부 환경)으로 이동하여 변수에 대한 외부 환경의 변수 환경을 확인합니다. 찾을 수없는 경우 스코프 체인을 계속 검색합니다. let과 const는 그렇지 않습니다.
let의 두 번째 기능은 블록 범위를 소개한다는 것입니다. 블록은 중괄호로 정의됩니다. 함수 블록, if 블록, 블록 등을 예로들 수 있습니다. 블록 내부에 let을 사용하여 변수를 선언하면 변수는 블록 내부에서만 사용할 수 있습니다. 실제로 for 루프 내에서와 같이 블록이 실행될 때마다 메모리에 새 변수가 생성됩니다.
ES6에는 변수 선언을위한 const 키워드도 도입되었습니다. const도 블록 범위입니다. let과 const의 차이점은 const 변수를 이니셜 라이저를 사용하여 선언해야한다는 점입니다. 그렇지 않으면 오류가 발생합니다.
마지막으로 실행 컨텍스트와 관련하여 var로 정의 된 변수는 'this'객체에 첨부됩니다. 전역 실행 컨텍스트에서는 브라우저의 창 개체가됩니다. let 또는 const의 경우에는 해당되지 않습니다.
나는 용어와 대부분의 예제가 약간 압도적이라고 생각한다. 내가 개인적으로 차이점을 가지고 있었던 주요 문제는 "블록"이 무엇인지 이해하는 것이다. 내가 깨달은 시점에서 블록은 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);
이제 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
}
사람들은 다른 언어, Java, C # 등과 같은 JavaScript에서 비슷한 범위를 갖도록 let을 사용하기 시작할 것이라고 생각합니다.
JavaScript의 범위 지정에 대해 명확하게 이해하지 못하는 사람들은 실수를 일찍 저 지르 곤했습니다.
를 사용하여 게양이 지원되지 않습니다 let
.
이 접근 방식으로 JavaScript에 존재하는 오류가 제거됩니다.
를 참조하십시오 ES6에서 깊이 :하자 CONST이 더 잘 이해 할 수 있습니다.
이 기사는 var, let 및 const의 차이점을 명확하게 정의합니다.
const
식별자가 재 할당되지 않음을 나타내는 신호입니다.
let
는 루프의 카운터 또는 알고리즘의 값 스왑과 같이 변수가 재 할당 될 수 있다는 신호입니다. 또한 변수가 정의 된 블록에서만 사용된다는 신호를 보냅니다. 항상 전체 포함 함수는 아닙니다.
var
JavaScript에서 변수를 정의 할 때 사용 가능한 가장 약한 신호입니다. 변수는 재 할당되거나 재 할당되지 않을 수 있으며, 변수는 전체 기능 또는 블록 또는 루프의 목적으로 사용될 수도 있고 사용되지 않을 수도 있습니다.
https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75#.esmkpbg9b
let
포함되며 최종 사양에 포함될 가능성이 높습니다.