>  기사  >  웹 프론트엔드  >  var에 대해 간략하게 이해하고 JavaScript 변수 또는 루프를 입력합니다.

var에 대해 간략하게 이해하고 JavaScript 변수 또는 루프를 입력합니다.

WBOY
WBOY앞으로
2022-09-14 17:18:441441검색

이 글은 javascript에 대한 관련 지식을 제공하며, 주로 var에 대한 자세한 설명과 JavaScript 변수 또는 루프에 대한 내용을 소개합니다. 이 글은 특정 참고 가치가 있는 주제를 중심으로 내용을 자세히 소개합니다. 한 번 보시고 모두에게 도움이 되길 바랍니다.

var에 대해 간략하게 이해하고 JavaScript 변수 또는 루프를 입력합니다.

[관련 권장 사항: javascript 비디오 튜토리얼, web front-end]

for 루프에서 var 선언 초기화를 사용하여 발생하는 문제

// 一道经典面试题:
var funcs = [];
for (var i = 0; i < 3; i++) {
  funcs[i] = function() {
    console.log("My value: " + i)
  };
}
for (var j = 0; j < 3; j++) {
  funcs[j]();
}
/*
输出结果:
> My value: 3
> My value: 3
> My value: 3
*/

이 현상이 발생하는 이유는 다음과 같습니다.

  • var 선언의 범위는 블록 수준 범위가 아닌 함수 범위이므로 for 루프를 초기화할 때 정의한 var 변수는 for 루프의 루프 본문 외부에서 계속 액세스할 수 있습니다.
  • 그리고 루프 종료 후 액세스할 때 액세스된 var 변수는 루프가 완료된 후의 값입니다.

해결책

클로저 사용

ES5 시대의 해결책은 IIFE를 통해 클로저를 만들고 함수 본문에 변수를 저장하는 것입니다. 함수가 실행될 때 외부 var 변수에 액세스하지 않습니다.

var funcs = [];
for (var i = 0; i < 3; i++) {
    // 1. 闭包
    funcs[i] = (function (i) {
        return function () {
            console.log("My value: " + i);
        };
    })(i);
}
for (var j = 0; j < 3; j++) {
    funcs[j]();
}

let 변수 초기화 사용

let 선언은 블록 수준 범위이며 루프 본문의 변수는 블록 문 외부로 유출되지 않습니다.

그래서 루프가 끝난 후 변수 i에 접근하면 외부 범위 변수의 간섭이 없으며, 접근되는 것은 당연히 함수 본문에 저장된 변수 값입니다.

var funcs = [];
// 2. let
for (let i = 0; i < 3; i++) {
  funcs[i] = function() {
    console.log("My value: " + i);
  };
}
for (var j = 0; j < 3; j++) {
  funcs[j]();
}

여기서 var를 사용하여 for 루프 자체를 초기화하는 것은 직관에 어긋난다는 것도 알 수 있습니다.

for 루프를 초기화하는 데 사용되는 변수는 for 루프의 로컬 변수여야 합니다. 이 변수는 루프가 끝난 후에는 의미가 없어야 합니다.

하지만 var를 사용하여 초기화하면 var가 선언한 변수의 범위가 함수 범위이므로 이 초기화 변수는 for 루프와 동일한 범위에 있고 for 루프에 의해 제한되지 않습니다.

for 루프의 로컬 변수여야 하는데 for 루프와 동일한 레이어의 범위에 노출되어 있고 루프 수에 따라 변수 값이 변경되어 자연스럽게 다른 접근에 영향을 주게 됩니다. 루프가 끝난 후 변수에 코드를 추가합니다.

let을 사용하여 for 루프를 초기화하면 이 문제가 발생하지 않습니다. 왜냐하면 let으로 선언된 범위는 블록 수준 범위이고 이 초기화 변수는 예상대로 for 루프의 로컬 변수가 되기 때문입니다.

for 루프는 let 및 var로 선언된 초기화 변수를 어떻게 처리하나요?

먼저 결론:

  • var로 초기화하면 for 루프는 생성된 var를 직접 사용하여 변수를 초기화합니다.
  • let으로 초기화하면 괄호가 자체 범위를 형성하고 for는 루프는 생성된 var를 사용하여 변수를 초기화합니다. 변수 값은 루프 본문으로 전달됩니다.

첫 번째 결론을 살펴보세요, 사양에는 다음과 같이 나와 있습니다.

보시다시피 사양에는 var 초기화 변수에 대한 특별한 처리가 없으며 직접 사용됩니다. 이때 이 변수는 일반적인 var 변수이며 for 루프와 동일한 범위에 있습니다.

코드를 사용하여 증명해 보겠습니다.

var funcs = [];
for (var i = 0; i < 3; i++) {
    // !!!重复声明了一个同名的var变量
    var i = 5;
    console.log("My value: " + i);
}
/*
只会输出一次:
> My value: 5
*/

var는 반복적으로 선언할 수 있으며 값을 덮어쓰게 되므로 루프 본문에 또 다른 var i = 5를 선언하고 루프 변수 for 루프에서 직접 빠져나옵니다.

var funcs = [];
for (var i = 0; i < 3; i++) {
    // 用let声明了一个和循环变量同名的变量
    let i = 5;
    console.log("My value: " + i);
}
/*
一共输出了3次:
> My value: 5
> My value: 5
> My value: 5
*/
var i = 5,循环变量被作没了,会直接跳出for循环。

var funcs = [];
for (let i = 0; i < 3; i++) {
    // !!!用let声明了一个和循环变量同名的变量
    let i = 5;
    console.log("My value: " + i);
}
/*
一共输出了3次:
> My value: 5
> My value: 5
> My value: 5
*/

初始化var变量在函数作用域,循环体内的let变量在块作用域,循环体内优先访问块作用域里的let变量,因此循环体内的i值会被覆盖。

又由于var变量实际上处于let变量的外层作用域,因此let变量没有重复声明,不会报错;var变量也会如期完成自己作为循环变量的使命。

再看第二个结论,同样是先看规范:

很明显可以发现,使用let来初始化会比使用var多了一个叫perIterationLets的东西。

perIterationLets是什么?

从规范上可以看到,perIterationLets来源于LexicalDeclaration(词法声明)里的boundNames

而这个LexicalDeclaration(词法声明),其实就是我们用来初始化的let声明。

可以理解为,如果我们用let声明来初始化for循环,for循环内部不会像直接使用var变量一样来直接使用let变量,而是会先把let变量收集起来,以某种形式转换为perIterationLets,再传递给循环体。

perIterationLets被用来做什么的?

从规范上可以看到,我们的let变量以perIterationLets的身份,作为参数被传进了ForBodyEvaluation,也就是循环体里。

在循环体里,perIterationLets只做了一件事情,那就是作为CreatePerIterationEnvironment초기화된 var 변수는 함수 범위에 있고, 루프 본문의 let 변수는 블록 범위에 있습니다. 루프 본문의 let 변수는 블록 범위의 let 변수에 액세스하는 우선순위를 가지므로 i 값은 루프 본문을 덮어쓰게 됩니다.

🎜그리고 var 변수는 실제로 let 변수의 외부 범위에 있기 때문에 let 변수는 반복적으로 선언되지 않으며 오류도 보고되지 않습니다. var 변수도 예정대로 루프 변수로서의 임무를 완료합니다. 🎜🎜🎜두 번째 결론을 살펴보고 사양도 먼저 살펴보세요. 🎜🎜🎜let을 사용하여 초기화하는 것은 var를 사용하는 것보다 perIterationLets라는 것이 하나 더 있다는 것이 분명합니다. 🎜🎜perIterationLets가 무엇인가요? 🎜🎜사양에서 볼 수 있듯이 perIterationLetsLexicalDeclaration(어휘 선언)boundNames에서 나옵니다. 🎜🎜그리고 이 LexicalDeclaration(어휘 선언)은 실제로 초기화에 사용하는 let 선언입니다. 🎜🎜let 문을 사용하여 for 루프를 초기화하면 let 변수는 var 변수처럼 for 루프 내에서 직접 사용되지 않고 대신 let 변수가 먼저 수집되어 perIterationLets 형식을 취한 다음 루프 본문에 전달됩니다. 🎜🎜perIterationLets는 🎜무엇에 사용되나요? 🎜🎜🎜let 변수가 루프 본문인 perIterationLets와 같은 매개변수로 ForBodyEvaluation에 전달된다는 것을 사양에서 볼 수 있습니다. 🎜🎜루프 본문에서 perIterationLets는 한 가지 작업, 즉 CreatePerIterationEnvironment의 매개변수로만 수행합니다. 🎜

从字面上理解,CreatePerIterationEnvironment意思就是每次循环都要创建的环境

要注意,这个环境不是{...}里的那些执行语句所处的环境。 {...}里的执行语句是statement,在规范里可以看到,stmt有自己的事情要做。

这个环境是属于圆括号的作用域,也就是我们定义的let初始化变量所在的作用域。

再看看每次循环都要创建的环境被用来干嘛了:

逐步分析一下方法:CreatePerIterationEnvironment这个

  • 首先,把当前执行上下文的词法环境保存下来,作为lastIterationEnv(上一次循环时的环境)
  • 创建一个和lastIterationEnv同级的新作用域,作为thisIterationEnv(本次循环的环境);
  • 遍历我们定义的let初始化变量,也就是perIterationLets,在thisIterationEnv(本次循环的环境)里创建一个同名的可变绑定,找到它们在lastIterationEnv(上一次循环时的环境)里的终值,作为这个同名绑定的初始值;
  • 最后,将thisIterationEnv(本次循环的环境)交还给执行上下文。

简而言之就是,for循环会在迭代之前创建一个和初始化变量同名的变量,并使用之前迭代的终值将这个变量初始化以后,再交还给执行上下文

用伪代码理解一下这个过程就是:

到这里又有一个问题,既然把圆括号内的变量向循环体里传递了,那如果在循环体里又重复声明了一个同名变量,算不算重复声明,会不会报错?

答案是不会。

因为CreatePerIterationEnvironment在执行时,在新环境里创建的是一个可变的绑定,因此如果在循环体内重复声明一个名字为i的变量,只是会影响循环体内执行语句对i值的访问。

var funcs = [];
for (let i = 0; i < 3; i++) {
    // !!!用let声明了一个和循环变量同名的变量
    let i = 5;
    console.log("My value: " + i);
}
/*
一共输出了3次:
> My value: 5
> My value: 5
> My value: 5
*/

总结

在for循环中使用var声明来初始化的话,循环变量会暴露在和for循环同一作用域下,导致循环结束后还能访问到循环变量,且访问到的变量值是经过循环迭代后的值。

解决这个问题的方法如下:

  • 使用闭包将循环变量的值作为局部变量保存起来;
  • 使用ES6的let声明,将循环变量的作用域限制在for循环内部,初始化变量始终是for循环的局部变量,不能在外界被访问到。

for循环是怎么处理用let和var声明的初始化变量的?

  • 用var初始化时,for循环会直接使用创建的var初始化变量;
  • 用let初始化时,圆括号会自成一个作用域,for循环会将圆括号内的变量值往循环体内传递。

【相关推荐:javascript视频教程web前端

위 내용은 var에 대해 간략하게 이해하고 JavaScript 변수 또는 루프를 입력합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 jb51.net에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제