web-dev-qa-db-ja.com

Javascript関数のスコープと巻き上げ

Ben CherryによるJavaScriptスコープとホイスト に関する素晴らしい記事を読んだところ、彼は次の例を示しています。

var a = 1;

function b() {
    a = 10;
    return;

    function a() {}
}
b();
alert(a);

上記のコードを使用すると、ブラウザは「1」を警告します。

なぜ「1」を返すのかはまだわかりません。彼が言うことのいくつかは、次のように思い浮かびます:すべての関数宣言は最上位に引き上げられます。関数を使用して変数のスコープを設定できます。それでもクリックしません。

86
dev.e.loper

関数ホイストとは、関数がスコープの最上部に移動することを意味します。あれは、

function b() {  
   a = 10;  
   return;  
   function a() {} 
} 

インターピーターによってこれに書き換えられます

function b() {
  function a() {}
  a = 10;
  return;
}

奇妙な、え?

また、この例では、

function a() {}

と同じように振る舞った

var a = function () {};

したがって、本質的に、これはコードが行っていることです:

var a = 1;                 //defines "a" in global scope
function b() {  
   var a = function () {}; //defines "a" in local scope 
   a = 10;                 //overwrites local variable "a"
   return;      
}       
b();       
alert(a);                 //alerts global variable "a"
113
Peter Olson

覚えておく必要があるのは、関数全体を解析し、実行する前にすべての変数宣言を解決することです。そう....

function a() {} 

本当になる

var a = function () {}

var aは、それをローカルスコープに強制し、変数スコープは関数全体を通過します。したがって、グローバル変数aは、関数にしてローカルスコープに宣言したため、まだ1です。

6
kemiller2002

関数aは、関数b内に巻き上げられます。

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

これはほとんどvarを使用するようなものです。

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

関数はローカルで宣言され、aの設定はグローバルvarではなくローカルスコープでのみ行われます。

5
Digital Plane
  1. 関数宣言function a(){}が最初に上げられ、var a = function () {};のように動作するため、ローカルスコープにaが作成されます。
  2. 同じ名前の2つの変数(1つはグローバル、もう1つはローカル)がある場合、ローカル変数は常にグローバル変数よりも優先されます。
  3. a=10を設定すると、グローバル変数ではなくローカル変数aが設定されます。

したがって、グローバル変数の値は同じままで、警告が表示されます1

3
Jhankar Mahbub

驚くべきことに、ここでの回答はどれもスコープチェーンの実行コンテキストの関連性について言及していません。

JavaScriptエンジンは、現在実行中のコードを実行コンテキストにラップします。基本実行コンテキストは、グローバル実行コンテキストです。新しい関数が呼び出されるたびに、新しい実行コンテキストが作成され、実行スタックに配置されます。他のプログラミング言語の呼び出しスタックにあるスタックフレームを考えてください。最初のうちの最後の。現在、各実行コンテキストには独自の変数環境とJavaScriptの外部環境があります。

以下の例をデモとして使用します。

1)最初に、グローバル実行コンテキストの作成フェーズに入ります。字句環境の外部環境と可変環境の両方が作成されます。グローバルオブジェクトがセットアップされ、特別な変数「this」がそれを指すようにメモリに配置されます。関数aとそのコード、および未定義の値を持つ変数myVarは、グローバル変数環境のメモリに配置されます。関数aのコードは実行されないことに注意することが重要です。関数aでメモリに配置されます。

2)次に、実行コンテキストの実行フェーズです。 myVarは未定義の値ではなくなりました。グローバル変数環境に保存されている値1で初期化されます。関数aが呼び出され、新しい実行コンテキストが作成されます。

3)関数aの実行コンテキストでは、独自の実行コンテキストの作成および実行フェーズを通過します。独自の外部環境と可変環境、したがって独自の語彙環境があります。関数bと変数myVarは、変数環境に保存されます。この変数環境は、グローバル変数環境とは異なります。関数aは、グローバル実行コンテキストと同じレベルに字句的に(物理的にコード内に)存在するため、その外部環境はグローバル実行コンテキストです。したがって、関数aが変数環境にない変数を参照する場合、スコープチェーンを検索し、グローバル実行コンテキストの変数環境で変数を見つけようとします。

4)関数aで関数bが呼び出されます。新しい実行コンテキストが作成されます。関数aに字句的に位置するため、その外部環境はaです。したがって、myVarを参照する場合、myVarは関数bの変数環境にないため、関数aの変数環境を調べます。そこで検出され、console.logが2を出力します。ただし、変数が関数aの変数環境にない場合、関数aの外部環境はグローバル実行コンテキストであるため、スコープチェーンはそこで検索を続けます。

5)関数bとaの実行が終了すると、実行スタックからポップされます。シングルスレッドJavaScriptエンジンは、グローバル実行コンテキストで実行を継続します。 b関数を呼び出します。ただし、グローバル変数環境にはb関数はなく、グローバル実行コンテキストで検索する外部環境はありません。したがって、JavaScriptエンジンによって例外が発生します。

function a(){
  function b(){
    console.log(myVar);
  }

  var myVar = 2;
  b();
}

var myVar = 1;
a();
b();
> 2
> Uncaught ReferenceError: b is not defined

以下の例は、動作中のスコープチェーンを示しています。関数bの実行コンテキストの変数環境には、myVarはありません。そのため、外部環境(関数a)を検索します。関数aの変数環境にもmyVarはありません。そのため、エンジン検索はグローバル実行コンテキストの外部環境であるaの外部環境を機能し、そこでmyVarが定義されます。したがって、console.logは1を出力します。

function a(){
  function b(){
    console.log(myVar);
  }

  b();
}

var myVar = 1;
a();
> 1

実行コンテキストと、それに関連する外部環境および変数環境を含む語彙環境に関して、JavaScriptで変数のスコープを有効にします。同じ関数を複数回呼び出した場合でも、呼び出しごとに独自の実行コンテキストが作成されます。そのため、各実行コンテキストには、変数環境に変数の独自のコピーがあります。変数の共有はありません。

1
Donato

巻き上げは、わかりやすくするために作成された概念です。実際に発生するのは、スコープに関して最初に宣言が行われ、その後に割り当てが行われることです(同時にではありません)。

宣言が発生すると、_var a_、次に_function b_、そしてそのbスコープ内で_function a_が宣言されます。

この関数aは、グローバルスコープからの変数aをシャドウします。

宣言が完了すると、値の割り当てが開始され、グローバルaが値_1_を取得し、内部の_function b_が_10_を取得します。 alert(a)を実行すると、実際のグローバルスコープ変数が呼び出されます。コードのこの小さな変更により、コードがより明確になります

_        var a = 1;

    function b() {
        a = 10;
        return a;

        function a() { }
    }

    alert(b());
    alert(a);
_
1
Buzzzzzzz

scpope&クロージャ&ホイスト(var/function)

  1. scpope:グローバル変数は任意の場所(ファイルスコープ全体)でアクセスでき、ローカル変数はローカルスコープ(関数/ブロックスコープ)のみがアクセスできます!
    注:ローカル変数が関数でvarキーワードを使用していない場合、グローバル変数になります!
  2. クロージャ:ローカルスコープ(親関数)とグローバルスコープにアクセスできる他の関数の内側の関数、しかし、その変数が他の人によってアクセスできない場合!場合を除き、戻り値としてそれを返す!
  3. 巻き上げ:値またはnullを割り当てるよりも、すべての宣言/宣言解除変数/関数をスコープ上部に移動します!
    注:値を移動するのではなく、宣言を移動するだけです!
var a = 1;                
//"a" is global scope
function b() {  
   var a = function () {}; 
   //"a" is local scope 
   var x = 12; 
   //"x" is local scope 
   a = 10;
   //global variable "a" was overwrited by the local variable "a"  
   console.log("local a =" + a);
   return console.log("local x = " + x);
}       
b();
// local a =10
// local x = 12
console.log("global a = " + a);
// global a = 1
console.log("can't access local x = \n");
// can't access local x = 
console.log(x);
// ReferenceError: x is not defined
1
xgqfrms

function a() { }は、a関数に対してローカルなb変数を作成する関数ステートメントです。
変数は、varまたは関数ステートメントが実行されるかどうかに関係なく、関数が解析されるときに作成されます。

a = 10は、このローカル変数を設定します。

1
SLaks

そのすべては、変数「a」のスコープに依存します。スコープを画像として作成して説明しましょう。

ここで、JavaScriptは3つのスコープを作成します。

i)グローバルスコープ。 ii)関数b() scope。iii)関数a() scope。

enter image description here

'alert'メソッドスコープを呼び出すとそのスコープがグローバルに属していることは明らかであるため、グローバルスコープから変数 'a'の値を選択するのは1だけです。

0
Sumit Pahuja

ここに、より多くの注釈と、いじくり回すのに役立つフィドルを使った回答の要約を示します。

// hoisting_example.js

// top of scope ie. global var a = 1
var a = 1;

// new scope due to js' functional (not block) level scope
function b() {
    a = 10; // if the function 'a' didn't exist in this scope, global a = 10
  return; // the return illustrates that function 'a' is hoisted to top
  function a(){}; // 'a' will be hoisted to top as var a = function(){};
}

// exec 'b' and you would expect to see a = 10 in subsequent alert
// but the interpreter acutally 'hoisted' the function 'a' within 'b' 
// and in doing so, created a new named variable 'a' 
// which is a function within b's scope
b();

// a will alert 1, see comment above
alert(a);

https://jsfiddle.net/adjavaherian/fffpxjx7/

0
4m1r

私の知る限りでは、変数宣言と関数宣言で巻き上げが発生します。たとえば、

a = 7;
var a;
console.log(a) 

JavaScriptのエンジン内で何が起こるか:

var a;
a = 7;
console.log(a);
// 7

または:

console.log(square(7)); // Output: 49
function square(n) { return n * n; }

次のようになります。

function square(n) { return n * n; }
console.log(square(7)); // 49

ただし、変数の割り当て、関数式の割り当てなどの割り当ては、引き上げられません。例:

console.log(x);
var x = 7; // undefined

次のようになります。

var x;
console.log(x); // undefined
x = 7;
0
Nam V. Do

変数名は関数名が「a」を意味するのと同じであるために発生しています。そのため、Javascriptを巻き上げるため、名前の競合を解決しようとし、a = 1を返します。

「JavaScriptホイスト」に関するこの投稿を読むまで、これについても混乱していました http://www.ufthelp.com/2014/11/JavaScript-Hoisting.html

それが役に立てば幸い。

0
AugustRush

ロングポスト!

しかし、それは空気をきれいにします!

Javaスクリプトが機能する方法は、2段階のプロセスを伴うことです。

  1. コンパイル(いわば)-このステップでは、変数と関数宣言、およびそれぞれのスコープを登録します。関数式の評価は含まれません:var a = function(){}または変数式(_3_の場合に_var x =3;_をxに割り当てることは、R.H.S部分の評価に他なりません。)

  2. 通訳者:これは実行/評価部分です。

以下のコードの出力を確認して、理解してください。

_//b() can be called here!
//c() cannot be called.
console.log("a is " + a);
console.log("b is " + b);
console.log("c is " + c);
var a = 1;
console.log("Now, a is " + a);
var c = function() {};
console.log("Now c is " + c);

function b() {
  //cannot write the below line:
  //console.log(e); 
  //since e is not declared.
  e = 10; //Java script interpreter after traversing from this function scope chain to global scope, is unable to find this variable and eventually initialises it with value 10 in global scope.
  console.log("e is " + e) //  works!
  console.log("f is " + f);
  var f = 7;
  console.log("Now f is " + f);
  console.log("d is " + d);
  return;

  function d() {}
}
b();
console.log(a);_

それを壊しましょう:

  1. コンパイルフェーズでは、「a」は値「undefined」でグローバルスコープに登録されます。 「c」についても同様です。この時点での値は「function()」ではなく「undefined」です。 'b'は、グローバルスコープの関数として登録されます。 bのスコープ内で、「f」はこの時点では未定義の変数として登録され、関数「d」は登録されます。

  2. インタプリタが実行されると、インタプリタが実際の式の行に到達する前に、宣言された変数とfunction()(式ではなく)にアクセスできます。そのため、変数は「undefined」と出力され、宣言された匿名関数はより早く呼び出すことができます。ただし、式の初期化の前に未宣言の変数にアクセスしようとすると、次のようなエラーが発生します。

_console.log(e)
e = 3;_

さて、同じ名前の変数と関数の宣言があるとどうなりますか。

Answer is-関数は常に前に巻き上げられ、同じ名前の変数が宣言されている場合、重複として扱われ、無視されます。順序は重要ではありません。関数は常に優先されます。ただし、評価段階では、変数参照を任意のものに変更できます(最後の割り当てが保存されます)。次のコードをご覧ください。

_var a = 1;
console.log("a is " + a);

function b() {
  console.log("a inside the function b is " + a); //interpreter finds                                'a' as function() in current scope. No need to go outside the scope to find 'a'.
  a = 3; //a changed
  console.log("Now a is " + a);
  return;

  function a() {}
}
var a; //treated as duplicate and ignored.
b();
console.log("a is still " + a + " in global scope"); //This is global scope a._
0
pragun

JavaScriptでは、変数の宣言は、コードが実行される前にプログラム全体で実行されます。したがって、コード内の任意の場所で変数を宣言することは、最初に変数を宣言することと同等です。

0
Vishwas S L

巻き上げはJavaScriptの動作概念です。ホイスト(移動など)は、変数を宣言する方法と場所を説明する概念です。

JavaScriptでは、関数の宣言と変数の宣言は常にJavaScriptインタープリターによって包含スコープの最上部に見えないように移動(「ホイスト」)されるため、変数は使用後に宣言できます。

ほとんどの場合、2種類の巻き上げが発生します。

1.変数宣言の巻き上げ

このコードでこれを理解しましょう。

 a = 5; // Assign 5 to a
 elem = document.getElementById("demo"); // Find an element 
 elem.innerHTML = a;                     // Display a in the element
 var a; // Declare a
  //output-> 5

ここで、変数aの宣言は、コンパイル時にjavascriptインタープリターによって目に見えないようにホストされます。したがって、aの値を取得できました。しかし、変数を宣言するこのアプローチは、このように既に変数をトップに宣言する必要があるため推奨されません。

 var a = 5; // Assign and declare 5 to a
 elem = document.getElementById("demo"); // Find an element 
 elem.innerHTML = a;                     // Display a in the element
  // output -> 5

別の例を考えてみましょう。

  function foo() {
     console.log(x)
     var x = 1;
 }

実際には次のように解釈されます:

  function foo() {
     var x;
     console.log(x)
     x = 1;
  }

この場合、xは未定義になります

変数の宣言を含むコードが実行されたかどうかは関係ありません。この例を考えてみましょう。

  function foo() {
     if (false) {
         var a = 1;
     }
     return;
     var b = 1;
  }

この関数は次のようになります。

  function foo() {
      var a, b;
      if (false) {
        a = 1;
     }
     return;
     b = 1;
  }

変数宣言では、代入ではなく変数定義のみがホイストされます。

  1. 関数宣言の巻き上げ

関数本体または割り当てられた値を巻き上げる変数とは異なり、巻き上げられます。このコードを検討してください

 function demo() {
     foo(); // this will give error because it is variable hoisting
     bar(); // "this will run!" as it is function hoisting
     var foo = function () {
         alert("this would not run!!");
     }
     function bar() { 
         alert("this will run!!");
     }
 }
 demo();

変数と関数の両方の巻き上げを理解したので、このコードを理解しましょう。

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

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

var a = 1;                 //defines "a" in global scope
 function b() {  
   var a = function () {}; //defines "a" in local scope 
    a = 10;                 //overwrites local variable "a"
    return;      
 }       
 b();       
 alert(a); 

関数a()はb()内にローカルスコープを持ちます。a()は、その定義でコードを解釈しながら、最上部に移動します(関数の巻き上げの場合)ので、aはローカルスコープを持つため、関数b()内に独自のスコープを持つ間はaのグローバルスコープに影響を与えません。

0
Mustkeem K