gpt4 book ai didi

javascript - JavaScript中变量的范围是什么?

转载 作者:行者123 更新时间:2023-11-29 16:04:13 25 4
gpt4 key购买 nike

javascript中变量的范围是什么?它们在函数内部和外部的作用域是否相同?还是有关系吗?另外,如果变量是全局定义的,则将变量存储在哪里?

最佳答案

TLDR
JavaScript具有词汇(也称为静态)作用域和闭包。这意味着您可以通过查看源代码来确定标识符的范围。
四个范围是:

  • 全局-一切可见
  • 函数-在函数(及其子函数和块)中可见
  • 块-在块(及其子块)中可见
  • 模块-在模块
  • 中可见

    在全局范围和模块范围的特殊情况之外,使用 var(函数范围), let(块范围)和 const(块范围)声明变量。标识符声明的大多数其他形式在严格模式下具有块作用域。
    总览
    范围是代码库中标识符有效的区域。
    词汇环境是标识符名称和与其关联的值之间的映射。
    范围由词汇环境的链接嵌套组成,嵌套中的每个级别对应于祖先执行上下文的词汇环境。
    这些链接的词汇环境形成了范围“链”。标识符解析是沿着此链搜索匹配标识符的过程。
    标识符解析仅在一个方向上发生:向外。这样,外部词汇环境无法“看到”内部词汇环境。
    在JavaScript中确定 scopeidentifier时需要考虑三个相关因素:
  • 如何声明标识符
  • 声明了标识符的位置
  • 无论您是strict mode还是non-strict mode

  • 可以声明标识符的一些方式:
  • varletconst
  • 函数参数
  • 捕获块参数
  • 函数声明
  • 命名函数表达式
  • 在全局对象上隐式定义的属性(即在非严格模式下丢失了var)
  • import语句
  • eval

  • 可以声明一些位置标识符:
  • 全局上下文
  • 函数主体
  • 普通块
  • 控件结构的顶部(例如,循环,if,while等)
  • 控制结构体
  • 模块

  • 声明样式
    变种
    使用 var 声明的标识符具有函数范围,除了直接在全局上下文中声明时,在这种情况下,它们被添加为全局对象的属性并具有全局范围。在 eval函数中有单独的使用规则。
    let和const
    使用 letconst 声明的标识符具有块范围,除了直接在全局上下文中声明时(在这种情况下,它们具有全局范围)。
    注意: letconstvar are all hoisted。这意味着它们的逻辑定义位置是其包围范围(模块或功能)的顶部。但是,在控件通过源代码中的声明点之前,无法读取或分配使用 letconst声明的变量。过渡期称为时间盲区。

    function f() {
    function g() {
    console.log(x)
    }
    let x = 1
    g()
    }
    f() // 1 because x is hoisted even though declared with `let`!

    功能参数名称
    函数参数名称的作用域为函数主体。请注意,这有点复杂。声明为默认参数的函数将关闭 parameter list,而不关闭函数的主体。
    函数声明
    函数声明在严格模式下具有块作用域,在非严格模式下具有函数作用域。注意:非严格模式是一组复杂的紧急规则,基于不同浏览器的古怪历史实现。
    命名函数表达式
    命名函数表达式的作用域本身(例如,出于递归目的)。
    全局对象上的隐式定义的属性
    在非严格模式下,全局对象上的隐式定义的属性具有全局范围,因为全局对象位于范围链的顶部。在严格模式下,这些是不允许的。
    评估
    eval字符串中,使用 var声明的变量将放置在当前作用域中,或者,如果间接使用 eval,则将其放置在全局对象上。
    例子
    以下内容将引发ReferenceError,因为名称 xyz在函数 f之外没有任何意义。

    function f() {
    var x = 1
    let y = 1
    const z = 1
    }
    console.log(typeof x) // undefined (because var has function scope!)
    console.log(typeof y) // undefined (because the body of the function is a block)
    console.log(typeof z) // undefined (because the body of the function is a block)

    以下内容将为 yz抛出ReferenceError,但不会为 x抛出ReferenceError,因为 x的可见性不受该块的限制。定义控制结构主体(如 ifforwhile)的块的行为类似。

    {
    var x = 1
    let y = 1
    const z = 1
    }
    console.log(x) // 1
    console.log(typeof y) // undefined because `y` has block scope
    console.log(typeof z) // undefined because `z` has block scope

    在下面的代码中,由于 x具有功能范围,因此 var在循环外部可见。

    for(var x = 0; x < 5; ++x) {}
    console.log(x) // 5 (note this is outside the loop!)

    ...由于这种行为,您需要谨慎使用循环中使用 var声明的变量。此处声明的变量 x仅有一个实例,并且在逻辑上位于循环之外。
    以下代码打印 5,五次,然后在循环外为 5第六次打印 console.log:

    for(var x = 0; x < 5; ++x) {
    setTimeout(() => console.log(x)) // closes over the `x` which is logically positioned at the top of the enclosing scope, above the loop
    }
    console.log(x) // note: visible outside the loop

    以下显示 undefined,因为 x是块作用域的。回调是异步进行的。 let变量的新行为意味着每个匿名函数都在名为 x的另一个变量上关闭(与 var不同),因此会打印整数 04

    for(let x = 0; x < 5; ++x) {
    setTimeout(() => console.log(x)) // `let` declarations are re-declared on a per-iteration basis, so the closures capture different variables
    }
    console.log(typeof x) // undefined

    下面的代码不会抛出 ReferenceError,因为 x的可见性不受该块的限制。但是,它将打印 undefined,因为尚未初始化变量(因为 if语句)。

    if(false) {
    var x = 1
    }
    console.log(x) // here, `x` has been declared, but not initialised

    使用 forlet循环顶部声明的变量的作用域为循环的主体:

    for(let x = 0; x < 10; ++x) {} 
    console.log(typeof x) // undefined, because `x` is block-scoped

    以下内容将抛出 ReferenceError,因为 x的可见性受该块的限制:

    if(false) {
    let x = 1
    }
    console.log(typeof x) // undefined, because `x` is block-scoped

    使用 varletconst声明的变量都作用于模块:
    // module1.js

    var x = 0
    export function f() {}

    //module2.js

    import f from 'module1.js'

    console.log(x) // throws ReferenceError
    以下内容将在全局对象上声明属性,因为在全局上下文中使用 var声明的变量将作为属性添加到全局对象:

    var x = 1
    console.log(window.hasOwnProperty('x')) // true

    全局上下文中的 letconst不会向全局对象添加属性,但仍具有全局作用域:

    let x = 1
    console.log(window.hasOwnProperty('x')) // false

    函数参数可以认为是在函数体中声明的:

    function f(x) {}
    console.log(typeof x) // undefined, because `x` is scoped to the function

    捕获块参数的作用域为捕获块主体:

    try {} catch(e) {}
    console.log(typeof e) // undefined, because `e` is scoped to the catch block

    命名函数表达式仅适用于表达式本身:

    (function foo() { console.log(foo) })()
    console.log(typeof foo) // undefined, because `foo` is scoped to its own expression

    在非严格模式下,全局对象上隐式定义的属性是全局范围的。在严格模式下,您会得到一个错误。

    x = 1 // implicitly defined property on the global object (no "var"!)

    console.log(x) // 1
    console.log(window.hasOwnProperty('x')) // true

    在非严格模式下,函数声明具有函数范围。在严格模式下,它们具有块作用域。

    'use strict'
    {
    function foo() {}
    }
    console.log(typeof foo) // undefined, because `foo` is block-scoped

    它是如何工作的
    范围定义为代码的 lexical区域,在该区域中标识符是有效的。
    在JavaScript中,每个功能对象都有一个隐藏的 [[Environment]]引用,该引用是对在其中创建它的 lexical environment(堆栈框架)的 execution context的引用。
    调用函数时,将调用隐藏的 [[Call]]方法。此方法创建一个新的执行上下文,并在新的执行上下文和功能对象的词法环境之间建立链接。通过将功能对象上的 [[Environment]]值复制到新执行上下文的词法环境上的 outer reference字段中,可以完成此操作。
    注意,新执行上下文和函数对象的词法环境之间的这种链接称为 closure
    因此,在JavaScript中,作用域是通过外部引用在“链”中链接在一起的词法环境实现的。这种词汇环境链称为作用域链,标识符解析由 searching up the chain进行,用于匹配标识符。
    找出 more

    关于javascript - JavaScript中变量的范围是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35732873/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com