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


当前回答

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

其他回答

最初由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的词法环境,这是调用时警报中引用的值。

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

全局作用域:全局只是一个窗口级作用域。在这里,整个应用程序中都存在变量。Functional Scope:在带有var关键字的函数中声明的变量具有函数作用域。

每当调用一个函数时,就会创建一个变量范围对象(并包含在范围链中),然后是JavaScript中的变量。

        a = "global";
         function outer(){ 
              b = "local";
              console.log(a+b); //"globallocal"
         }
outer();

作用域链-->

窗口级别-a和外部函数位于范围链的顶层。当外部函数调用一个新的变量作用域对象(并包含在作用域链中)时,在其内部添加了变量b。

现在,当一个变量a需要时,它首先搜索最近的变量范围,如果变量不在,则将其移动到变量范围链的下一个对象。在这种情况下,这是窗口级别。

JS中只有函数作用域。不阻止范围!你也可以看到什么在提升。

var global_variable = "global_variable";
var hoisting_variable = "global_hoist";

// Global variables printed
console.log("global_scope: - global_variable: " + global_variable);
console.log("global_scope: - hoisting_variable: " + hoisting_variable);

if (true) {
    // The variable block will be global, on true condition.
    var block = "block";
}
console.log("global_scope: - block: " + block);

function local_function() {
    var local_variable = "local_variable";
    console.log("local_scope: - local_variable: " + local_variable);
    console.log("local_scope: - global_variable: " + global_variable);
    console.log("local_scope: - block: " + block);
    // The hoisting_variable is undefined at the moment.
    console.log("local_scope: - hoisting_variable: " + hoisting_variable);

    var hoisting_variable = "local_hoist";
    // The hoisting_variable is now set as a local one.
    console.log("local_scope: - hoisting_variable: " + hoisting_variable);
}

local_function();

// No variable in a separate function is visible into the global scope.
console.log("global_scope: - local_variable: " + local_variable);

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

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

内联处理程序

前端代码编写者经常遇到的一个尚未描述的非常常见的问题是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内部的顶层类似。模块可以引用任何全局变量,除非模块为其明确设计,否则任何内容都不能引用模块内部的任何内容。