ホームページ  >  記事  >  ウェブフロントエンド  >  var を簡単に理解して、JavaScript 変数またはループを導入する

var を簡単に理解して、JavaScript 変数またはループを導入する

WBOY
WBOY転載
2022-09-14 17:18:441457ブラウズ

この記事は、javascript に関する関連知識を提供するもので、主に var と let in JavaScript の変数やループの詳細な説明を紹介しており、テーマを中心に詳細な内容を紹介しています。値については、以下で見てみましょう。皆様のお役に立てれば幸いです。

var を簡単に理解して、JavaScript 変数またはループを導入する

[関連する推奨事項: JavaScript ビデオ チュートリアル Web フロントエンド ]

で var を使用します。 for ループ 宣言の初期化によって引き起こされる問題

// 一道经典面试题:
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を通じてクロージャを作成し、変数を関数本体に保存してから、 function 外側の 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 変数であり、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 ループが直接飛び出します。 <pre class="brush:js;">var funcs = []; for (var i = 0; i &lt; 3; i++) { // 用let声明了一个和循环变量同名的变量 let i = 5; console.log(&quot;My value: &quot; + i); } /* 一共输出了3次: &gt; My value: 5 &gt; My value: 5 &gt; My value: 5 */</pre>関数スコープで var 変数を初期化し、ループ本体の let 変数はブロック スコープにあります。ループ本体の let 変数はブロック スコープの let 変数に優先的にアクセスします。ループ本体の i 値は上書きされます。

そして、var 変数は実際には let 変数の外側のスコープ内にあるため、let 変数は繰り返し宣言されず、エラーは報告されません。var 変数も予定どおりループ変数としての使命を完了します。

2 番目の結論を見てみましょう。最初に仕様も見てみましょう:

let を使用して初期化すると、var を使用するよりも呼び出し ## が 1 つ多くなるのは明らかです。 #perIterationLets

のもの。

perIterationLets

それは何ですか?

仕様からわかるように、perIterationLets

LexicalDeclaration (字句宣言) boundNames から来ています。 そして、この LexicalDeclaration (字句宣言)

は、実際には初期化に使用する let ステートメントです。

let ステートメントを使用して for ループを初期化すると、let 変数は var 変数のように for ループ内で直接使用されず、代わりに let 変数が最初に収集され、フォームは perIterationLets

に変換され、ループ本体に渡されます。

perIterationLets

は何に使用されますか? 仕様からわかるように、let 変数は、ループ本体である perIterationLets

としてパラメータとして

ForBodyEvaluation に渡されます。 ループ本体では、perIterationLets

は 1 つのことのみを実行します。つまり、

CreatePerIterationEnvironment のパラメーターとして実行します。<p>从字面上理解,<code>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 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjb51.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。