ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript 変数スコープの例の紹介

JavaScript 変数スコープの例の紹介

零下一度
零下一度オリジナル
2017-06-28 13:47:211247ブラウズ

この記事では主に JavaScript 変数スコープを紹介します。編集者が非常に優れていると考えたので、参考として共有します。エディターに従って見てみましょう

JavaScript では、var で宣言された変数には実際にスコープがあります。 var申明的变量实际上是有作用域的。

如果一个变量在函数体内部申明,则该变量的作用域为整个函数体,在函数体外不可引用该变量:


'use strict';

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

x = x + 2; // ReferenceError! 无法在函数体外引用变量x

如果两个不同的函数各自申明了同一个变量,那么该变量只在各自的函数体内起作用。换句话说,不同函数内部的同名变量互相独立,互不影响:


'use strict';

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

function bar() {
  var x = 'A';
  x = x + 'B';
}

由于JavaScript的函数可以嵌套,此时,内部函数可以访问外部函数定义的变量,反过来则不行:


'use strict';

function foo() {
  var x = 1;
  function bar() {
    var y = x + 1; // bar可以访问foo的变量x!
  }
  var z = y + 1; // ReferenceError! foo不可以访问bar的变量y!
}

如果内部函数和外部函数的变量名重名怎么办?


'use strict';

function foo() {
  var x = 1;
  function bar() {
    var x = 'A';
    alert('x in bar() = ' + x); // 'A'
  }
  alert('x in foo() = ' + x); // 1
  bar();
}

这说明JavaScript的函数在查找变量时从自身函数定义开始,从“内”向“外”查找。如果内部函数定义了与外部函数重名的变量,则内部函数的变量将“屏蔽”外部函数的变量。

变量提升

JavaScript的函数定义有个特点,它会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部:


'use strict';

function foo() {
  var x = 'Hello, ' + y;
  alert(x);
  var y = 'Bob';
}

foo();

虽然是strict模式,但语句var x = 'Hello, ' + y;并不报错,原因是变量y在稍后申明了。但是alert显示Hello, undefined,说明变量y的值为undefined。这正是因为JavaScript引擎自动提升了变量y的声明,但不会提升变量y的赋值。

对于上述foo()函数,JavaScript引擎看到的代码相当于:


function foo() {
  var y; // 提升变量y的申明
  var x = 'Hello, ' + y;
  alert(x);
  y = 'Bob';
}

由于JavaScript的这一怪异的“特性”,我们在函数内部定义变量时,请严格遵守“在函数内部首先申明所有变量”这一规则。最常见的做法是用一个var申明函数内部用到的所有变量:


function foo() {
  var
    x = 1, // x初始化为1
    y = x + 1, // y初始化为2
    z, i; // z和i为undefined
  // 其他语句:
  for (i=0; i<100; i++) {
    ...
  }
}

全局作用域

不在任何函数内定义的变量就具有全局作用域。实际上,JavaScript默认有一个全局对象window,全局作用域的变量实际上被绑定到window的一个属性:


&#39;use strict&#39;;

var course = &#39;Learn JavaScript&#39;;
alert(course); // &#39;Learn JavaScript&#39;
alert(window.course); // &#39;Learn JavaScript&#39;

因此,直接访问全局变量course和访问window.course是完全一样的。

你可能猜到了,由于函数定义有两种方式,以变量方式var foo = function () {}定义的函数实际上也是一个全局变量,因此,顶层函数的定义也被视为一个全局变量,并绑定到window对象:


&#39;use strict&#39;;

function foo() {
  alert(&#39;foo&#39;);
}

foo(); // 直接调用foo()
window.foo(); // 通过window.foo()调用

进一步大胆地猜测,我们每次直接调用的alert()函数其实也是window的一个变量:


&#39;use strict&#39;;

window.alert(&#39;调用window.alert()&#39;);
// 把alert保存到另一个变量:
var old_alert = window.alert;
// 给alert赋一个新函数:
window.alert = function () {}

// 恢复alert:
window.alert = old_alert;
alert(&#39;又可以用alert()了!&#39;);

这说明JavaScript实际上只有一个全局作用域。任何变量(函数也视为变量),如果没有在当前函数作用域中找到,就会继续往上查找,最后如果在全局作用域中也没有找到,则报ReferenceError错误。

名字空间

全局变量会绑定到window上,不同的JavaScript文件如果使用了相同的全局变量,或者定义了相同名字的顶层函数,都会造成命名冲突,并且很难被发现。

减少冲突的一个方法是把自己的所有变量和函数全部绑定到一个全局变量中。例如:


// 唯一的全局变量MYAPP:
var MYAPP = {};

// 其他变量:
MYAPP.name = &#39;myapp&#39;;
MYAPP.version = 1.0;

// 其他函数:
MYAPP.foo = function () {
  return &#39;foo&#39;;
};

把自己的代码全部放入唯一的名字空间MYAPP中,会大大减少全局变量冲突的可能。

许多著名的JavaScript库都是这么干的:jQuery,YUI,underscore等等。

局部作用域

由于JavaScript的变量作用域实际上是函数内部,我们在for

変数が関数本体内で宣言されている場合、変数

のスコープは関数全体ですbody の場合、関数本体の外で変数を参照することはできません:


&#39;use strict&#39;;

function foo() {
  for (var i=0; i<100; i++) {
    //
  }
  i += 100; // 仍然可以引用变量i
}

2 つの異なる関数が同じ変数を宣言している場合、変数はそれぞれの関数の本体内でのみ機能します。つまり、異なる関数内の同じ名前の変数は互いに独立しており、相互に影響しません。

&#39;use strict&#39;;

function foo() {
  var sum = 0;
  for (let i=0; i<100; i++) {
    sum += i;
  }
  i += 1; // SyntaxError
}

JavaScript 関数はネストできるため、現時点では、
内部関数

は、外部関数によって定義された変数にアクセスできますが、その逆はできません: 🎜🎜🎜🎜🎜
var PI = 3.14;
🎜 内部関数の変数名が外部関数は同じ名前ですか? 🎜🎜🎜🎜🎜
&#39;use strict&#39;;

const PI = 3.14;
PI = 3; // 某些浏览器不报错,但是无效果!
PI; // 3.14
🎜 これは、JavaScript 関数が変数を検索するときに独自の関数定義から開始し、「内側」から「外側」に向かって検索することを示しています。内部関数が外部関数と同じ名前の変数を定義している場合、内部関数の変数は外部関数の変数を「シールド」します。 🎜🎜🎜変数のプロモーション🎜🎜🎜🎜JavaScriptの関数定義には、まず関数本体全体のステートメントをスキャンし、宣言されたすべての変数を関数の先頭に「プロモート」します。は strict モードですが、変数 y が後で宣言されるため、ステートメント var x = 'Hello, ' + y; はエラーを報告しません。ただし、alert には Hello, unknown が表示され、変数 y の値が unknown であることが示されます。これは、JavaScript エンジンが変数 y の宣言を自動的に促進しますが、変数 y の割り当てを促進しないためです。 🎜🎜🎜上記の foo() 関数の場合、JavaScript エンジンによって認識されるコードは次と同等です: 🎜🎜🎜🎜🎜rrreee🎜 JavaScript のこの奇妙な「機能」により、関数内では次のようになります。 変数🎜を定義する場合は、「すべての変数を関数内で最初に宣言する」というルールを厳守してください。最も一般的なアプローチは、var を使用して、関数内で使用されるすべての変数を宣言することです。エリア。実際、JavaScript にはデフォルトでグローバル オブジェクト window があり、グローバル スコープ内の変数は実際には window のプロパティにバインドされています: 🎜🎜🎜🎜🎜rrreee🎜 したがって、グローバル変数 course に直接アクセスすることは、window.course にアクセスすることとまったく同じです。 🎜🎜🎜関数を定義するには 2 つの方法があるため、変数モード var foo = function () {} で定義された関数は実際にはグローバル変数であると推測したかもしれません。したがって、次の定義になります。トップレベル関数 これはグローバル変数とみなされ、window オブジェクトにバインドされます: 🎜🎜🎜🎜🎜rrreee🎜 さらに大胆な推測ですが、毎回直接呼び出すalert()関数は実際にはwindow の変数:🎜🎜🎜🎜🎜rrreee🎜 これは、JavaScript が実際には 1 つのグローバル スコープしか持たないことを示しています。現在の 関数スコープ 🎜 で見つからない変数 (関数も変数とみなされます) は、検索を続行します。最後に、グローバル スコープで見つからない場合は、ReferenceError が報告されます。 🎜🎜🎜🎜名前空間🎜🎜🎜🎜 グローバル変数は window にバインドされます。異なる JavaScript ファイルが同じグローバル変数を使用したり、同じ名前のトップレベル関数を定義したりすると、名前付けの問題が発生します。競合が発生し、検出が困難になります。 🎜🎜🎜競合を減らす 1 つの方法は、すべての変数と関数をグローバル変数にバインドすることです。例: 🎜🎜🎜🎜🎜rrreee🎜すべてのコードを一意の名前空間 MYAPP に配置します。これにより、グローバル変数の競合の可能性が大幅に減少します。 🎜🎜🎜jQuery、YUI、アンダースコアなど、多くの有名な JavaScript ライブラリがこれを実行します。 🎜🎜🎜🎜ローカルスコープ🎜🎜🎜🎜JavaScriptの変数スコープは実際には関数内にあるため、forループなどのステートメントブロックでローカルスコープの変数を定義することはできません: 🎜 🎜🎜🎜🎜rrreee 🎜ブロックレベルのスコープの問題を解決するために、ES6 では新しいキーワード let を導入しました。ブロックレベルのスコープ変数を宣言するには、var の代わりに let を使用します。

由于varlet申明的是变量,如果要申明一个常量,在ES6之前是不行的,我们通常用全部大写的变量来表示“这是一个常量,不要修改它的值”:


var PI = 3.14;

ES6标准引入了新的关键字const来定义常量,const与let都具有块级作用域:


&#39;use strict&#39;;

const PI = 3.14;
PI = 3; // 某些浏览器不报错,但是无效果!
PI; // 3.14

以上がJavaScript 変数スコープの例の紹介の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。