JavaScript에서 for 문장의 초기화 부분에서 let으로 발표된 변수는 순환마다 다른 실례를 가질 수 있는 이유

9864 단어 JavaScript
JavaScript에서 for 문장의 초기화 부분에서 let으로 발표한 변수는 왜 순환마다 다른 실례를 가지고 있습니까?관련하여 조사하였다.
발단
사내 슬랙내@i9i의 발언에 따르면.

확실히 자바와 C의 경우 변수 i의 실례가 순서대로 늘어나기 때문에 이런 의문은 자연히 상승할 것이다.
조사하다.
이해가 안 돼서 규격을 조사한 for문의 기재입니다.
14.7.4.2 Runtime Semantics: ForLoopEvaluation
포문
for ( Expression opt ; Expression opt ; Expression opt ) Statement
for ( var VariableDeclarationList ; Expression opt ; Expression opt ) Statement
for ( LexicalDeclaration Expression opt ; Expression opt ) Statement
이번이 세 번째 포문이다.
for 문장의 평가
for문은 다음과 같이 평가된다.
(참조보다 링크 대상이 읽기 쉽습니다. 참조)
ForStatement : for ( LexicalDeclaration Expressionopt ; Expressionopt ) Statement
1. Let oldEnv be the running execution context's LexicalEnvironment.
2. Let loopEnv be NewDeclarativeEnvironment(oldEnv).
3. Let isConst be IsConstantDeclaration of LexicalDeclaration.
4. Let boundNames be the BoundNames of LexicalDeclaration.
5. For each element dn of boundNames, do
 a. If isConst is true, then
  i. Perform ! loopEnv.CreateImmutableBinding(dn, true).
 b. Else,
  i. Perform ! loopEnv.CreateMutableBinding(dn, false).
6. Set the running execution context's LexicalEnvironment to loopEnv.
7. Let forDcl be the result of evaluating LexicalDeclaration.
8. If forDcl is an abrupt completion, then
 a. Set the running execution context's LexicalEnvironment to oldEnv.
 b. Return Completion(forDcl).
9. If isConst is false, let perIterationLets be boundNames; otherwise let perIterationLets be « ».
10. Let bodyResult be ForBodyEvaluation(the first Expression, the second Expression, Statement, perIterationLets, labelSet).
11. Set the running execution context's LexicalEnvironment to oldEnv.
12. Return Completion(bodyResult).
여기 있습니다.를 참고하십시오.이것은
  • Let env be a new declarative Environment Record containing no bindings.
  • Set env.[[OuterEnv]] to E.
  • Return env.
  • 이러한 동작은 Environment Record가 순환에 들어갈 때 만들어진다(Environment는 뒤에 서술한다).
    그다음5.그리고 2.를 참고하십시오.이것은 NewDeclarativeEnvirionment(oldEnv)에서 진행된다.
    이 Environment는 6입니다.에서 컨텍스트를 실행하는 LexicalEnvironment가 됩니다.
    그다음순환에 들어가다.
    for 순환 내부 처리
    순환 중의 처리는이름 묶음(Wikipedia)이다.
  • Let V be undefined.
  • Perform ? CreatePerIterationEnvironment(perIterationBindings).
  • Repeat,
  •  a. If test is not [empty], then
      i. Let testRef be the result of evaluating test
      ii. Let testValue be ? GetValue(testRef)
      iii. If ! ToBoolean(testValue) is false, return NormalCompletion(V).
     b. Let result be the result of evaluating stmt.
     c. If LoopContinues(result, labelSet) is false, return Completion(UpdateEmpty(result, V)).
     d. If result.[[Value]] is not empty, set V to result.[[Value]].
     e. Perform ? CreatePerIterationEnvironment(perIterationBindings).
     f. If increment is not [empty], then
      i. Let incRef be the result of evaluating increment.
     ii. Perform ? GetValue(incRef).
    2. 및 3-e.CreateMutableBinding()에서 수행합니다(여기서).
    14.7.4.3 ForBodyEvaluation(test, increment, stmt, perIterationBindings, labelSet)
  • If perIterationBindings has any elements, then
  •  a. Let lastIterationEnv be the running execution context's LexicalEnvironment.
     b. Let outer be lastIterationEnv.[[OuterEnv]].
     c. Assert: outer is not null.
     d. Let thisIterationEnv be NewDeclarativeEnvironment(outer).
     e. For each element bn of perIterationBindings, do
      i. Perform ! thisIterationEnv.CreateMutableBinding(bn, false).
      ii. Let lastValue be ? lastIterationEnv.GetBindingValue(bn, true).
      iii. Perform thisIterationEnv.InitializeBinding(bn, lastValue).
     f. Set the running execution context's LexicalEnvironment to thisIterationEnv.
    2. Return undefined.
    1-a. 실행 중인 LexicalEnvironment는 lastIteration Env입니다.순환의 시작은 순환 내의 2이다.Environment를 만들었고, 순환 도중 이전 순환으로 만든 Environment가 됐다.
    1-d.CreatePerIterationEnvironment(perIterationBindings)에서 1-e-i. 새로 만든 Envirinment에서 실행NewDeclarativeEnvirionment(outer)합니다.이로써 모든perIterationBindings의 표지부호, 즉 for문장의 초기화 부분에서let이 선포한 변수의 표지부호에 대응하는 값을 생성한다.즉, 서로 다른 Environment에서 각 순환과 식별자에 대응하는 값은 각각 다른 실례로 만들어진다.
    또한, 여기서 outer는 1-b.에 따라 이전 순환의 outer가 된다.순환이 시작된 상황에서 outer는 링 밖의 Environment이기 때문에 앞으로 outer는 링 밖의 Environment를 계속 참조할 것이다.
    Environment Records
    여기, Environment: 환경이지만 환경은 14.7.4.4 CreatePerIterationEnvironment(perIterationBindings)에서 설명되어 있습니다.
    다음은 간단한 환경에 대한 설명입니다.
    function f() {
        let i = 1;
        function g() {
          let j = 2;
          return i + j;
        };
        return g;
    }
    
    함수 g()의 환경
    Environment Records
    실행에서 참조할 수 있는 식별자와 해당 식별자에 대응하는 값의 집합을 나타낸다.
    실행 중인 함수에 따라 이 실행 중인 함수를 정의한 함수 내(외부 환경)의 변수도 자유 변수 참조(프록시 용접 가능)로 사용할 수 있기 때문에 이미지와 같은 구조를 형성한다.
    순환 내부 처리CreateMutableBinding()에서 outer(전번 순환할 때의 환경의'외부 환경')를'외부 환경'으로 삼아 새로운 환경을 만들었기 때문에 모든 순환의 환경은

    모습
    결론
    이러한 방식으로 순환 초기화 부분에서let으로 발표한 변수는 서로 다른 실례이다. 왜냐하면 모든 순환이 환경을 만들고 그 중에서 변수와 값을 만들기 때문이다.
    이로써 유명한 for문 초기화 부분에서var의 선언과let의 선언 행위의 차이로
    const functions = [];
    for (var i = 0; i < 3; i++) {
      functions.push(() => console.log(i));
    }
    functions[0]();
    functions[1]();
    functions[2]();
    
    결과
    3
    3
    3
    
    다음을 클릭합니다.
    const functions = [];
    for (let i = 0; i < 3; i++) {
      functions.push(() => console.log(i));
    }
    functions[0]();
    functions[1]();
    functions[2]();
    
    결과
    0
    1
    2
    
    이 점도 이해할 수 있다.세 개의 for문 중에서 두 번째 문장을 따라잡는 처리 차이가 매우 뚜렷하다.
    ( )

    좋은 웹페이지 즐겨찾기