ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript のスコープ設定とホイスティング 翻訳_JavaScript スキル

JavaScript のスコープ設定とホイスティング 翻訳_JavaScript スキル

WBOY
WBOYオリジナル
2016-05-16 17:52:081149ブラウズ

次の JavaScript コードを実行するとアラートがどのような値を出力するかご存知ですか?

コードをコピーします コードは次のとおりです。

var foo = 1; bar() {
if (!foo) {
var foo = 10>}
alert(foo);

;
「10」という答えに驚いた場合は、さらに混乱するかもしれません:
[/code]
var a = 1;
function b() {
a = 10 ;
return;
function a() {}
}
alert(a);
[/code]
ブラウザは「1」を警告します。それで、何が起こったのでしょうか?これは少し奇妙で、少し危険で、少し混乱しているように見えるかもしれませんが、実際には、この言語の強力な表現機能です。この動作を定義する標準があるかどうかはわかりませんが、私はそれを説明するために「ホイスティング」を使用することを好みます。この記事ではこのメカニズムを説明しますが、その前に、JavaScript のスコープについて必要な理解をしておきましょう。
JavaScript のスコープ
スコープは、JavaScript の初心者にとって最もわかりにくい部分の 1 つです。実際、初心者だけでなく、スコープを完全に理解できない経験豊富な JavaScript プログラマーにもたくさん会いました。 JavaScript のスコープが非常に複雑である理由は、JavaScript が C 言語ファミリーのメンバーに非常によく似ているためです。次の C プログラムを見てください:




コードをコピーします
コードは次のとおりです: #include int main() { int x = 1; // 1
if (1); 🎜>int x = 2;
printf("%d, ", x); // 2
}
printf("%dn", x); // 1
}


このプログラムの出力は 1,2,1 です。これは、C シリーズ言語にはブロック レベルのスコープがあり、if ステートメントと同様に、新しい変数がこのブロック レベルのスコープで宣言されるためです。これらの変数は外側のスコープには影響しません。しかし、JavaScript の場合はそうではありません。 Firebug で次のコードを試してください:




コードをコピーします


コードは次のとおりです:
var x = 1; console.log(x); // 1 if (true) { console.log(x); console.log(x);// 2

このコードでは、Firebug は 1、2、2 を表示します。これは、JavaScript には関数レベルのスコープがあるためです。これは C ベースの言語とはまったく異なります。 if ステートメントと同様、ブロックは新しいスコープを作成しません。新しいスコープを作成するのは関数のみです。
C、C#、C#、または Java に精通しているほとんどのプログラマにとって、これは予期せぬことであり、歓迎されないことです。幸いなことに、JavaScript 関数には柔軟性があるため、この問題には解決策があります。関数内に一時的なスコープを作成する必要がある場合は、次のように実行します:




コードをコピーします

コードは次のとおりです:

function foo() { var x = 1; if (x) { (function () { var x = 2; //他のコード }());
}
// x は 1 のままです。
}


この側面は確かに非常に柔軟で、次のように使用できます。ブロック内だけでなく、一時的なスコープの場所。ただし、時間をかけて JavaScript のスコープを理解することを強くお勧めします。これは本当に強力で、この言語の私のお気に入りの機能の 1 つです。スコーピングをよく理解するとホイスティングも理解しやすくなります。
宣言、名前、ホイスティング
JavaScript では、スコープ内に 4 つのタイプの名前があります。
1. 言語定義: すべての関数 フィールドには、デフォルトで this と引数が含まれます。
2. 仮パラメータ: 名前付きの関数パラメータは関数本体のスコープに入ります。
3. 関数の宣言: 関数 foo() {} の形式。
4. 変数宣言: var foo; の形式。
関数宣言と変数宣言は、JavaScript インタプリタによって常にそれらを含むスコープの先頭に暗黙的にホイストされます。明らかに、言語自体の定義と関数パラメーターはすでにスコープの最上位にあります。これは次のコードのようなものです:




コードをコピーします


コードは次のとおりです:

関数foo() { bar(); var x = 1;
は実際には次のように解釈されます:
コードをコピー コードは次のとおりです:

function foo() {
var x;
bar();


結果は、ステートメントが実行されるかどうか。次の 2 つのコードは同等です:


コードをコピーします コードは次のとおりです:
関数 foo () {
if (false) {
var x = 1;
}
return;
関数 foo() {
var x, y;
if (false) {
x = 1;
y = 1;
宣言の代入部分がホイストされていないことに注意してください。宣言された名前のみが昇格されます。これは、関数本体全体もホイストされる関数宣言とは異なります。ただし、関数を宣言するには通常 2 つの方法があることに注意してください。次の JavaScript コードを考えてみましょう:




コードをコピーします

コードは次のとおりです:

関数テスト() { foo(); // TypeError "foo は関数ではありません" bar() // "これは実行されます!" var foo = function () { // 関数式は変数 'foo' に割り当てられます alert("これは実行されません!"); } function bar() { // 'bar' という名前の関数宣言
alert("これは実行されますrun! ");
}
}
test();


ここでは、関数宣言のみが関数本体と一緒にプロモートされ、関数式のみがプロモートされます。 be昇格 名前と関数本体は、代入ステートメントの実行時にのみ割り当てられます。
上記は巻き上げの基本をすべて網羅していますが、それほど複雑でわかりにくいとは思えませんね。ただし、これは JavaScript であり、特殊な場合には常に少し複雑な問題が発生します。
名前解決の順序
覚えておくべき最も重要な特殊ケースは、名前解決の順序です。名前をスコープに入れるには 4 つの方法があることに注意してください。上にリストした順序は、解析される順序です。一般に、名前がすでに定義されている場合、その名前は、異なる属性を持つ同じ名前の別の名前によって上書きされることはありません。これは、関数宣言が変数宣言よりも優先されることを意味します。ただし、これはこの名前への割り当てが無効であるという意味ではなく、宣言された部分が無視されるというだけです。いくつかの例外があります:
組み込みの名前引数は少し奇妙な動作をします。仮引数の後、関数宣言の前に宣言されているようです。これは、パラメータが未定義であっても、arguments という名前の仮パラメータが組み込み引数よりも優先されることを意味します。これは悪い機能です。引数を仮パラメータとして使用しないでください。
これを識別子として使用しようとすると構文エラーが発生しますが、これは良い機能です。
同じ名前の仮パラメータが複数ある場合、たとえ未定義であっても、リストの最後にあるパラメータが最も高い優先度を持ちます。
関数式に名前を付ける
関数宣言ステートメントと同じように、関数式で関数の名前を定義できます。ただし、これは関数宣言にはならず、名前はスコープに導入されず、関数本体はホイストされません。意味を説明するためのコードを次に示します。




コードをコピー


コードは次のとおりです:

foo( ); // TypeError "foo は関数ではありません" bar(); // 有効な baz(); // TypeError "baz は関数ではありません" spam();スパムは定義されていません" var foo = function () {}; // 匿名関数式 ('foo' が昇格されます) function bar() {}; // 関数宣言 ('bar ' および関数body は昇格されます) var baz = function spam() {}; // 名前付き関数式 ('baz' のみが昇格されます)
foo() // 有効
bar();
baz(); // 有効な
spam(); // ReferenceError "スパムが定義されていません"


この知識を使ってコーディングする方法
これで理解できました。スコープとホイスティング、これは JavaScript コードの記述にとって何を意味しますか?最も重要なことは、変数を宣言するときに常に var ステートメントを使用することです。各スコープの先頭では 1 つの var のみを使用することを強くお勧めします。これを無理にやっておけば、昇進に関する問題に悩まされることはありません。ただし、そうすることで、現在のスコープでどの変数が実際に宣言されているかを追跡することがより困難になります。 JSLint では onevar オプションを使用することをお勧めします。これまでの提案をすべて実行すると、コードは次のようになります:




コードをコピーします

コードは次のようになります:

/*jslint onevar: true [...] */
function foo(a, b, c) {
var x = 1,
bar,
baz = "something";
}

標準の内容
これらのことがどのように行われるかを理解するには、ECMAScript 標準 (pdf) を直接参照することが常に役立ちます。働け、働ける。以下は、変数宣言とスコープ (セクション 12.2.2) に関する抜粋です:
変数ステートメントが FunctionDeclaration 内で発生する場合、セクション 10.1.3 で説明されているように、変数はその関数内の関数ローカル スコープで定義されます。それ以外の場合、変数は、ブロックの実行スコープに入ったときにプロパティ属性 { DontDelete } を使用してグローバル スコープで定義されます (つまり、セクション 10.1.3 で説明されているように、グローバル オブジェクトのメンバーとして作成されます)。新しい実行スコープを定義しません。新しいスコープを生成する変数は、Initialiser を持つ変数の AssignmentExpression の値が、変数の作成時ではなく、実行時に割り当てられます。 🎜>
この記事が JavaScript プログラマーの最も混乱する部分に光を当てることができれば幸いです。これ以上混乱を招かないように、包括的に書くよう最善を尽くしました。間違いを犯したり、重要な点を見逃したりした場合は、お知らせください。
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。