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


当前回答

据我理解,关键是Javascript具有函数级作用域,而不是更常见的C块作用域。

这是一篇关于这个主题的好文章。

其他回答

我很喜欢接受的答案,但我想补充一点:

作用域收集并维护所有声明的标识符(变量)的查找列表,并对当前执行的代码如何访问这些标识符实施一组严格的规则。

作用域是一组按标识符名称查找变量的规则。

如果在直接作用域中找不到变量,引擎将继续查询下一个外部包含作用域,直到找到或到达最外部(即全局)作用域。是一组规则,用于确定在何处以及如何查找变量(标识符)。该查找可能用于分配变量,该变量是LHS(左侧)参考,也可能用于检索其值,该值是RHS(右侧)参考。LHS引用源于赋值操作。与作用域相关的赋值可以使用=运算符进行,也可以通过将参数传递给(赋值给)函数参数进行。JavaScript引擎在执行代码之前首先编译代码,在这样做的过程中,它会拆分像vara=2这样的语句;分为两个单独的步骤:第一步。首先,var a在该范围中声明它。这是在代码执行之前开始执行的。第二。稍后,a=2查找变量(LHS引用),如果找到,则将其赋值。LHS和RHS引用查找都从当前执行的作用域开始,如果需要的话(也就是说,它们找不到它们在那里寻找的对象),它们会在嵌套作用域中一次一个作用域(层)查找标识符,直到它们到达全局(顶层)并停止,然后要么找到,要么不找到。未完成的RHS引用导致引发ReferenceError。未完成的LHS引用将导致该名称的自动隐式创建的全局(如果不在严格模式下),或ReferenceError(如果在严格模式中)。作用域由一系列“气泡”组成,每个气泡充当容器或桶,其中声明了标识符(变量、函数)。这些气泡整齐地嵌套在彼此内部,这种嵌套是在作者时定义的。

最初由Brendan Eich设计的JavaScript中的作用域概念来自HyperCard脚本语言HyperTalk。

在这种语言中,显示类似于一堆索引卡。有一张主卡被称为背景。它是透明的,可以看作是底牌。此基础卡上的任何内容都与放在其上面的卡共享。放在上面的每张卡都有自己的内容,这些内容优先于前一张卡,但如果需要,仍然可以访问前一张卡片。

这正是JavaScript作用域系统的设计方式。它只是有不同的名字。JavaScript中的卡片称为Execution ContextsCMA。这些上下文中的每一个都包含三个主要部分。变量环境、词汇环境和此绑定。回到卡片参考,词汇环境包含堆栈中较低的先前卡片的所有内容。当前上下文位于堆栈的顶部,其中声明的任何内容都将存储在变量环境中。在命名冲突的情况下,变量环境将优先。

此绑定将指向包含对象。有时,作用域或执行上下文在包含对象不变的情况下发生变化,例如在声明的函数中,包含对象可能是窗口或构造函数。

这些执行上下文是在任何时候传输控制时创建的。当代码开始执行时,控制权被转移,这主要是通过函数执行完成的。

这就是技术解释。在实践中,记住在JavaScript中

范围在技术上是“执行上下文”上下文形成存储变量的环境堆栈堆栈的顶部优先(底部是全局上下文)每个函数都创建一个执行上下文(但不总是一个新的此绑定)

将此应用于前面的一个示例(5。“闭包”),可以遵循执行上下文的堆栈。在本例中,堆栈中有三个上下文。它们由外部上下文、var six调用的立即调用函数中的上下文以及var six立即调用函数内部返回函数的上下文定义。

i) 外部上下文。它的可变环境为a=1ii)IIFE上下文,它有一个a=1的词法环境,但一个a=6的变量环境在堆栈中优先iii)返回的函数上下文,它具有a=6的词法环境,这是调用时警报中引用的值。

ECMAScript 6引入了let和const关键字。这些关键字可以用来代替var关键字。与var关键字相反,let和const关键字支持在块语句中声明局部范围。

var x = 10
let y = 10
const z = 10
{
  x = 20
  let y = 20
  const z = 20
  {
    x = 30
    // x is in the global scope because of the 'var' keyword
    let y = 30
    // y is in the local scope because of the 'let' keyword
    const z = 30
    // z is in the local scope because of the 'const' keyword
    console.log(x) // 30
    console.log(y) // 30
    console.log(z) // 30
  }
  console.log(x) // 30
  console.log(y) // 20
  console.log(z) // 20
}

console.log(x) // 30
console.log(y) // 10
console.log(z) // 10

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

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

(函数foo(){console.log(foo)})();console.log(foo类型);//未定义,因为“foo”的作用域是它自己的表达式//但是,像这样(函数foo(){console.log('1:',foo)//函数foofoo=100console.log('2:',foo)//函数foo,不是100,为什么?})()