최근에 ES2015 연습을보고 있습니다.
JavaScript에는 블록 레벨 범위가 없습니다
이 문제를 이해하지 못할 수도 있습니다. 먼저 예를 살펴 보겠습니다.
var a = [] for (var i = 0; i <10; i ++) {a [i] = function () {console.log (i); }} a [6] ();많은 사람들 이이 질문의 결과가 6이라고 생각하지만 불행히도 대답은 10입니다. 다른 것을 시도하십시오. A [7] (), A [8] () 및 [8] () 모두 10이 있습니다!
JS는 종종 var str = "hello world"; str.slice (1)에 대해 처리 할 때 종종 원시 변수를 해당 객체로 랩합니다.
JS의 실제 과정은 아마도 var str = "hello world"; new String (str) .slice (1) 일 것입니다. 이러한 과정은 우리가 문제를 이해하는 데 어려움을 줄 수 있습니다.
이 문제를 여기서 설명하기 위해, 나는 원시 유형의 숫자 유형에 속합니다. 나는 그것을 숫자 유형으로 명시 적으로 선언합니다. 기본 유형의 할당 프로세스는 메모리를 다시 적용하고 변수의 방향을 수정하는 것이므로,이 프로세스를 시뮬레이션하기 위해 새로운 숫자 객체의 프로세스를 사용합니다. 수정 된 코드는 다음과 같습니다.
var a = [] var i = 새 번호 (0); for (; i <10; i = 새 번호 (i+1)) {a [i] = function () {console.log (i.toString ()); }} a [6] (); // 10a [7] (); // 10a [8] (); // 10a [9] (); // 10a [9] (); // 10프로그램과 함께 이러한 변수의 상대 메모리 주소를 살펴 보겠습니다.
(function () {var id = 0; function generateId () {return id ++;}; object.prototyp.id = function () {var newid = generateD (); this.id = function () {return newid;}; return newid;};}) (); var a = [] var i = new Number (0); new <); i); // 0,; 숫자 (i+1), i.id ()) {a [i] = function () {console.log (i.id ()); console.log (i.toString ()); }} a [6] (); // 10 10A [7] (); // 10 10A [8] (); // 10 10A [9] (); // 10 10console.log (i.id ()) // 10여기서 우리는 실제로 I의 전체 "과제"효과를 시뮬레이션했으며 I의 상대 주소는 0에서 10으로 변경되었습니다 (결국 For 루프에서 점프하기 전에 한 번 추가해야합니다).
i의 상대 주소를 보면서, 우리는 문제를 발견했습니다 : [x] (x : 0 ~ 9)에 해당하는 함수가 실행될 때, i 참조 된 상대 주소는 10입니다. 왜?
여기서는 블록 레벨 범위 문제가 포함됩니다. 여기서 우리는 ES6에 대한 소개에서 Ruan Yifeng의 구절을 인용합니다.
ES5에는 글로벌 범위 및 기능 범위 만 있지만 블록 레벨 범위는 없습니다.
ES5는 JS의 가장 널리 사용되는 버전입니다. 이 문장은 JavaScript에서는 블록 범위가 없다고 말합니다. 글로벌 범위와 블록 수준 범위 만 있습니다.
이해하는 방법? 예를 들어
for (var i = 0; i <10; i ++) {console.log (i);} console.log (i); // 10console.log (Window.i); // 10직관적으로, 우리는 for 루프가 코드 블록이며 블록 레벨 범위에 속해야한다고 생각합니다. 그러나 정상적으로 0 ~ 9를 출력 할 수있을뿐만 아니라 FOR 루프에서 외부 적으로 10을 출력 할 수도 있습니다. 동시에, 우리는 for 루프에서 i를 정의했지만 글로벌 창 객체에 매달려있는 것 같습니다 (Nodejs의 실행 환경 인 경우 글로벌 객체에 매달려있을 것입니다).
따라서 JavaScript의 루프와 같은 블록은 블록 레벨 범위의 영향을 미치지 않습니다. 루프와 같은 코드 블록에서 변수를 정의하는 것은 현재 범위에서 변수를 직접 정의하는 것과 다르지 않습니다.
그러나 우리는 기능을 통해 범위를 분리 할 수 있습니다.
(함수 () {for (var i = 0; i <10; i ++) {console.log (i);} console.log (i);}) () console.log (i); /// i는 정의되지 않았습니다.동시에 Console.log (Window.i) 인 경우; 실행되면 정의되지 않은 결과가 얻어집니다. 여기서 우리는 즉각적인 실행 함수를 사용하여 범위를 형성합니다. 코드 블록과 유사한 역할을합니다. 이 함수 범위 후에는 더 이상 액세스 할 수없는 변수에 액세스 할 수 없습니다. 그러나 함수 범위 내에서 언제든지 액세스 할 수 있습니다.
이전 질문으로 돌아가서 JavaScript의 글로벌 범위 및 블록 수준 범위와 함께이를 이해합니다. for 루프에서, I 정의 된 I는 현재 범위, 즉 창 범위로 정의되어야합니다. 루프 본체에서는 기능을 [i]에 할당합니다. 이 기능을 실행할 때 상황은 다음과 같습니다.
나는이 함수에 존재하지 않으므로 스코프 체인을 따라 i를 찾습니다. 이 시점에서 우리가 출력하는 것은 이것이 i입니다. 루프의 마지막 +1에서 뛰어 내리므로 10이되므로 출력 결과는 항상 10이됩니다. 그러나 우리가 실제로 필요한 것은 마지막 I가 아니라 중간 프로세스에서 I입니다. 이 문제를 해결하려면 변수 I을 제쳐두어야합니다 (마지막 I가 필연적으로 10이되기 때문에). [0]에 해당하는 함수가 값 0을 참조하고 [1]에 해당하는 함수가 값 1을 참조하도록해야합니다. 아래 그림과 같이.
이전 코드로 돌아갑니다.
그림의 화살표는 I (0 ~ 9)에 액세스 할 수 있음을 보여줍니다. 여기서 For Loop은 자체적으로 블록 레벨 범위를 형성하지 않기 때문에 스코프 체인을 따라갈 때 For Loop에 의해 정의 된 I에 액세스합니다.
여기서 우리는 즉각적인 실행 함수로 코드를 랩핑하여 범위를 형성 할 수 있으며 값을 전달합니다. 다음 :
var a = [] var i = 새 번호 (0); console.log (i.id ()); // 0for (; i <10; i = new 숫자 (i+1)) {(function (i) {a [i] = function () {console.log (i.id ()); console.log (i.toString ()) // 6 6a [7] (); // 7 7a [8] (); // 8 8a [9] (); // 9 9Console.log (i.id ()); // 10}이 즉각적인 실행 함수는 함수 A [i]를 실행할 때 숫자 값 0 ~ 9를 나타 내기 때문에 먼저 스코프 체인을 따라 즉시 실행 함수의 범위를 찾습니다. 즉각적인 실행 함수는 0 ~ 9에서 숫자 참조를 유지하며 기능 a [i] 함수에서 i의 값을 올바르게 출력 할 수 있습니다. 실행 결과를 통해 실행 결과뿐만 아니라 참조 값의 상대 메모리 주소도 정확하다는 것을 알 수 있습니다. 그런 다음 원래 테스트를 위해 명시 적으로 선언 된 숫자 객체를 변경합니다. 다음과 같이 :
var a = []; for (var i = 0; i <10; i ++) {(함수 (i) {a [i] = function () {console.log (i)}}) (i);}마지막으로, var 대신 LET를 사용하여 권장되는 ES6 구문을 살펴보고 Baable을 통해 ES5 코드를 컴파일하고 생성하는 것을 살펴 보겠습니다.
// es6 code var a = [] for (i = 0; i <10; i ++) {a [i] = function () {console.log (i); }} a [6] (); // babel이 컴파일되고 생성 된 es5 코드 "strict 사용"; var a = []; var _loop = function _loop (i) {a [i] = function () {console.log (i); };}; for (var i = 0; i <10; i ++) {_loop (i);} a [6] ();당사의 솔루션이 ES6 솔루션과 매우 유사한 지 확인해 봅시다. 여기서 우리의 즉각적인 실행 함수는 생성 된 ES5 코드에서 _Loop 함수 및 _Loop (i)의 실행과 동일합니다.