ホームページ  >  記事  >  ウェブフロントエンド  >  Javascript のスコープと変数の深い理解 Promotion_JavaScript スキル

Javascript のスコープと変数の深い理解 Promotion_JavaScript スキル

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

次のプログラムの結果は何ですか?

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

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

結果は 10 です

次の場合はどうでしょうか?

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

var a = 1;
function b() {
a = 10;
return;
function a() {}
}
b();
alert(a);

結果は 1 です。

ショックを受けましたか?どうしたの?これは奇妙で、危険で、混乱を招くかもしれませんが、実際には非常に便利で印象的な JavaScript 言語機能でもあります。この動作に標準的な名前があるかどうかはわかりませんが、私はこの「ホイスティング」という用語が好きです。この記事ではこの仕組みについて入門的に説明しますが、その前に JavaScript の範囲について必要な理解をしておきましょう。

JavaScript の範囲

JavaScript の初心者にとって、最も混乱する点の 1 つはスコープです。実際、これは初心者だけではありません。私は何人かの経験豊富な JavaScript プログラマーに会ったことがありますが、彼らはスコープについて深く理解していません。 JavaScript のスコープがわかりにくい理由は、次の C プログラムのように、プログラム構文自体が C ファミリ言語に似ているためです:

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

#include
int main() {
int x = 1;
printf("%d, ", x); // 1
if (1) {
int x = 2;
printf("%d, ", x); // 2
}
printf( "% dn", x); // 1
}

出力結果は 1 2 1 になります。これは、C ファミリ言語にはブロック スコープがあるためです。 If ブロックなど、ブロック外のスコープには影響を与えずに、ブロック内にのみ影響を与える変数を宣言できます。しかし、JavaScript ではこれは機能しません。以下のコードを見てください:
コードをコピーします コードは次のとおりです:

var x = 1;
console.log(x); // 1
if (true) {
var x = 2;
console.log(x); // 2
}
console.log(x); // 2

結果は 1 2 2 になります。 JavaScriptは関数スコープなので。これが C 言語ファミリーとの最大の違いです。このプログラムの if は新しいスコープを作成しません。

多くの C、C、Java プログラマーにとって、これは期待し歓迎するものではありません。幸いなことに、JavaScript 関数には柔軟性があるため、これを回避する方法があります。一時的なスコープを作成する必要がある場合は、次のように作成できます:

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

function foo() {
var x = 1;
if (x) {
(function () {
var x = 2;
// some other code
}());
}
// x は 1 のままです。
}

このメソッドは非常に柔軟で、どこでも使用できます。一時的なスコープの場所を作成します。ブロック内だけではありません。ただし、時間をかけて JavaScript のスコープを理解することを強くお勧めします。これは非常に便利で、JavaScript の私のお気に入りの機能の 1 つです。スコープを理解していれば、変数のホイスティングがより理解できるでしょう。

変数の宣言、名前付け、プロモーション

JavaScript では、変数がスコープに入るには 4 つの基本的な方法があります。

•1 組み込み言語: すべてのスコープに this と引数があります (翻訳者注: テスト後、引数はグローバル スコープでは表示されません)

•2 仮パラメータ: 関数の仮パラメータは関数本体のスコープの一部になります。

•3 関数宣言: 次のような形式: function foo(){};

•4 変数宣言: 次のように: var foo

関数宣言と変数宣言は、インタプリタによって常に静かにメソッド本体の先頭に「昇格」されます。これは、次のコードのようになります。


function foo( ) {
bar();
var x = 1;
}


は実際には次のように解釈されます:
コードをコピーします コードは次のとおりです:

function foo () {
var x;
bar();
x = 1;
}

変数が定義されているブロックに関係なく実行することができます。次の 2 つの関数は実際には同じものです:
コードをコピーします コードは次のとおりです:

function foo() {
if (false) {
var x = 1;
}
return;
var y = 1;
}
function foo( ) {
var x, y;
if (false) {
x = 1;
}
return;
y = 1;
}

変数の割り当てはホイストされず、宣言のみであることに注意してください。ただし、関数の宣言は少し異なり、関数本体も昇格されます。ただし、関数を宣言するには 2 つの方法があることに注意してください:
コードをコピーします コードは次のとおりです:

function test() {
foo(); // TypeError "foo は関数ではありません"
bar() // "これは実行されます!"
var foo = function (); { // 関数を指す変数expression
alter("this will't run!");
}
function bar() { // 関数宣言関数名は bar
alter("this実行されます!" );
}
}
test();

この例では、関数宣言のみが関数本体と一緒にプロモートされます。 foo の宣言はホイストされますが、それが指す関数本体は実行中にのみ割り当てられます。

上記ではブーストの基本の一部を説明していますが、それほど混乱するものではないようです。ただし、一部の特殊なシナリオでは、依然としてある程度の複雑さが存在します。

変数の解析順序

心に留めておくべき最も重要なことは、変数解決の順序です。先ほど説明した、命名が範囲に入る 4 つの方法を覚えていますか?変数が解析される順序は、リストした順序です。

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

<script><br>関数 a (){ <br>}<br>var a;<br>alert(a);//a<br></script> の関数本体を出力します。

var a;
function a(){
}
alert(a);//a の関数本体を出力します

//ただし、注意 以下の 2 つの書き方との違いを区別してください:
<script><br>var a=1;<br>function a(){ <br>}<br>alert(a);//print out 1<br></script>

<script><br>function a(){ <br>}</p> <p>var a=1;</p> <p>alert(a);//print out 1<br></script>


ここには 3 つの例外があります:

1 組み込みの名前引数は、関数の仮パラメータの後、関数宣言の前に宣言する必要があるようです。これは、仮パラメータに引数がある場合、それが組み込みパラメータよりも優先されることを意味します。これは非常に悪い機能なので、仮パラメータでの引数の使用は避けてください。

2 この変数を任意の場所で定義すると構文エラーが発生しますが、これは良い機能です。

3 複数の仮パラメータが同じ名前である場合、実際の操作中にその値が未定義であっても、最後のパラメータが優先されます。

名前付き関数

関数に名前を付けることができます。その場合、それは関数宣言ではなく、関数本体定義内の指定された関数名 (以下のスパムなど、翻訳者注記がある場合) は昇格されず、無視されます。理解に役立つコードを次に示します。


コードをコピーします コードは次のとおりです:
foo(); // TypeError "foo は関数ではありません"
bar(); // 有効な
baz(); // TypeError "baz は関数ではありません"
spam(); / ReferenceError "スパムが定義されていません"

var foo = function () {}; // foo は匿名関数を指します function bar() {} // 関数宣言

var baz = function spam() {};機能では、baz のみが昇格され、スパムは昇格されません。

foo(); // 有効な

bar(); // 有効な

spam(); // 参照エラー "スパムが定義されていません"


コードの書き方

スコープと変数ホイスティングについては理解できましたが、これは JavaScript コーディングにとって何を意味するのでしょうか?最も重要なことは、変数を常に var で定義することです。また、名前については、スコープ内に var 宣言を常に 1 つだけ含めることを強くお勧めします。これを行うと、スコープと変数のホイスティングの問題は発生しなくなります。

言語仕様の言い方

ECMAScript のリファレンス ドキュメントがいつも役に立ちます。スコープと変数ホイスティングについて私が発見したことは次のとおりです:

変数が関数本体クラスで宣言されている場合、それは関数スコープです。それ以外の場合は、(グローバルのプロパティとして) グローバルにスコープ設定されます。変数は、実行がスコープに入ると作成されます。ブロックは新しいスコープを定義せず、関数宣言とプロシージャ (翻訳者はグローバル コード実行であると考える) のみが新しいスコープを作成します。変数は作成時に未定義に初期化されます。変数宣言ステートメントに代入操作がある場合、代入操作は作成時ではなく実行時にのみ発生します。

この記事が、JavaScript について混乱しているプログラマーに一筋の光をもたらすことを願っています。私もこれ以上混乱を招かないように最善を尽くします。私が何か間違ったことを言ったり、何か見落としたりした場合は、お知らせください。

訳者補足

友人が、IE のグローバル スコープで名前付き関数を改善するという問題を発見したことを思い出させてくれました。

これは記事を翻訳するときにテストしたものです:

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

<script><br>functiont(){<br>spam();<br>var baz = function spam() {alert('これはスパムです')};<br>}<br>t() ;<br></script>

この書き方、つまり非グローバルスコープでの名前付き関数のプロモーションは、IE と FF で同じパフォーマンスを示します。これを次のように変更しました:
コードをコピー コードは次のとおりです:

spam();
var baz = function spam() {alert('this is spam')};


この場合、スパムはie では実行されますが、ff では実行されません。 説明 によって、この詳細が異なります。

この質問により、他の 2 つの質問についても考えるようになりました。 1: スコープ上でグローバルに動作する変数の場合、var と非 var の間に違いがあり、var がないと、変数は昇格されません。たとえば、次の 2 つのプログラムのうち、2 番目のプログラムはエラーを報告します。

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

<script><br>alert(a);<br>var a=1;<br></script>

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

<script><br>alert(a);<br>a =1;<br></script>

2: eval で作成されたローカル変数は昇格されません (昇格する方法がありません)。
コードをコピー コードは次のとおりです:

<script><br>var a = 1;<br>function t( ){<br> アラート(a);<br> eval('var a = 2');<br> アラート(a);<br>}<br>t();<br>アラート(a); <br></script>
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。