javascript中变量的范围是什么?它们在函数内部的作用域与在函数外部的作用域相同吗?或者这有关系吗?此外,如果变量是全局定义的,那么变量存储在哪里?


当前回答

在“Javascript 1.7”(Mozilla对Javascript的扩展)中,还可以使用let语句声明块范围变量:

 var a = 4;
 let (a = 3) {
   alert(a); // 3
 }
 alert(a);   // 4

其他回答

JavaScript中有两种类型的作用域。

全局范围:在全局范围中宣布的变量可以在程序中的任何地方非常流畅地使用。例如:var carName=“BMW”;//这里的代码可以使用carName函数myFunction(){//这里的代码可以使用carName}函数作用域或局部作用域:在此作用域中声明的变量只能在其自身的函数中使用。例如://这里的代码不能使用carName函数myFunction(){var carName=“BMW”;//这里的代码可以使用carName}

在EcmaScript5中,主要有两个作用域,局部作用域和全局作用域,但在EcmaScript6中,我们主要有三个作用域:局部作用域、全局作用域和一个称为块作用域的新作用域。

块范围示例如下:-

for ( let i = 0; i < 10; i++)
{
 statement1...
statement2...// inside this scope we can access the value of i, if we want to access the value of i outside for loop it will give undefined.
}

内联处理程序

前端代码编写者经常遇到的一个尚未描述的非常常见的问题是HTML中内联事件处理程序可见的范围,例如

<button onclick="foo()"></button>

on*属性可以引用的变量范围必须是:

全局(工作内联处理程序几乎总是引用全局变量)文档的属性(例如,作为独立变量的querySelector将指向document.querySelector;罕见)处理程序附加到的元素的属性(如上所述;罕见)

否则,当调用处理程序时,将得到ReferenceError。因此,例如,如果内联处理程序引用了在window.onload或$(function){中定义的函数,则引用将失败,因为内联处理程序只能引用全局范围内的变量,而函数不是全局的:

window.addEventListener('DOMContentLoaded',()=>{函数foo(){console.log('运行');}});<button onclick=“foo()”>单击</button>

文档的财产和处理程序附加到的元素的财产也可以作为内联处理程序中的独立变量引用,因为内联处理函数是在两个块中调用的,一个用于文档,另一个用于元素。这些处理程序中变量的作用域链非常不直观,并且一个工作的事件处理程序可能需要一个函数是全局的(并且应该避免不必要的全局污染)。

由于内联处理程序内部的作用域链非常奇怪,而且内联处理程序需要全局污染才能工作,并且内联处理程序在传递参数时有时需要丑陋的字符串转义,因此可能更容易避免它们。相反,使用Javascript(如addEventListener)而不是HTML标记附加事件处理程序。

函数foo(){console.log('运行');}document.querySelector('.my button').addEventListener('click',foo);<button class=“my button”>单击</button>

模块(<script type=“module”>)

另一方面,与在顶层运行的普通<script>标记不同,ES6模块内的代码在其自己的私有范围内运行。在普通<script>标记顶部定义的变量是全局的,因此您可以在其他<script>标签中引用它,如下所示:

<脚本>const foo=“foo”;</script><脚本>console.log(foo);</script>

但是ES6模块的顶层不是全局的。在ES6模块顶部声明的变量仅在该模块内部可见,除非该变量被显式导出,或者除非它被分配给全局对象的属性。

<script type=“module”>const foo=“foo”;</script><脚本>//无法在此处访问foo,因为其他脚本是模块console.log(foo类型);</script>

ES6模块的顶层与普通<script>中顶层IIFE内部的顶层类似。模块可以引用任何全局变量,除非模块为其明确设计,否则任何内容都不能引用模块内部的任何内容。

下面是一个示例:

<script>

var globalVariable = 7; //==window.globalVariable

function aGlobal( param ) { //==window.aGlobal(); 
                            //param is only accessible in this function
  var scopedToFunction = {
    //can't be accessed outside of this function

    nested : 3 //accessible by: scopedToFunction.nested
  };

  anotherGlobal = {
    //global because there's no `var`
  }; 

}

</script>

您将需要调查闭包,以及如何使用它们创建私有成员。

几乎只有两种类型的JavaScript作用域:

每个var声明的范围都与最直接的封闭函数相关联如果var声明没有封闭函数,则为全局范围

因此,除函数之外的任何块都不会创建新的作用域。这解释了for循环覆盖外部范围变量的原因:

var i = 10, v = 10;
for (var i = 0; i < 5; i++) { var v = 5; }
console.log(i, v);
// output 5 5

改用函数:

var i = 10, v = 10;
$.each([0, 1, 2, 3, 4], function(i) { var v = 5; });
console.log(i,v);
// output 10 10

在第一个示例中,没有块范围,因此最初声明的变量被覆盖。在第二个示例中,由于函数的原因,出现了一个新的作用域,因此最初声明的变量是SHADOWED,而不是重写。

就JavaScript范围而言,这几乎是您需要了解的全部内容,除了:

try/catch仅为异常变量本身引入新范围,其他变量没有新范围with子句显然是另一个例外,但不鼓励使用with子句(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with)

因此,您可以看到JavaScript作用域实际上非常简单,尽管并不总是直观的。需要注意的几点:

var声明被提到了范围的顶端。这意味着无论var声明发生在哪里,对于编译器来说,就像var本身发生在顶部一样同一范围内的多个var声明组合在一起

因此,此代码:

var i = 1;
function abc() {
  i = 2;
  var i = 3;
}
console.log(i);     // outputs 1

相当于:

var i = 1;
function abc() {
  var i;     // var declaration moved to the top of the scope
  i = 2;
  i = 3;     // the assignment stays where it is
}
console.log(i);

这似乎与直觉相悖,但从命令式语言设计者的角度来看,这是有道理的。