8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

JavaScript 中变量的范围是什么?

MTilsted 1月前

127 0

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

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

帖子版权声明 1、本帖标题:JavaScript 中变量的范围是什么?
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由MTilsted在本站《function》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 总长DR

    JavaScript 具有词法(也称为静态)作用域和闭包。这意味着您可以通过查看源代码来判断标识符的作用域。

    这四个范围是:

    1. 全局 - 一切皆可见
    2. 功能 - 在功能(及其子功能和块)内可见
    3. 块 - 在块(及其子块)内可见
    4. 模块 - 在模块内可见

    除了全局和模块作用域的特殊情况外,变量使用 var (函数作用域)、 let (块作用域)和 const (块作用域)声明。大多数其他形式的标识符声明在严格模式下都具有块作用域。

    概述

    范围是标识符有效的代码库区域。

    词汇环境是标识符名称和与其关联的值之间的映射。

    范围由链接的词法环境嵌套组成,嵌套中的每一级对应于祖先执行上下文的词法环境。

    这些链接的词汇环境形成一个范围“链”。标识符解析是沿着此链搜索匹配标识符的过程。

    标识符解析仅发生在一个方向:向外。这样,外部词汇环境就无法“看到”内部词汇环境。

    JavaScript 中 标识符 范围 有三个相关因素

    1. 如何声明标识符
    2. 标识符声明的位置
    3. 无论你是处于 严格模式 or 非严格模式

    声明标识符的一些方法如下:

    1. var , let const
    2. 函数参数
    3. 捕获块参数
    4. 函数声明
    5. 命名函数表达式
    6. 在非严格模式下 var 缺失
    7. import 语句
    8. eval

    可以声明一些位置标识符:

    1. 全球背景
    2. 函数主体
    3. 普通方块
    4. 控制结构的顶部(例如 loop、if、while 等)
    5. 控制结构体
    6. 模块

    声明样式

    曾是

    使用 声明的标识符 var 具有函数作用域 ,除非直接在全局上下文中声明它们,在这种情况下,它们将作为属性添加到全局对象上并具有全局作用域。在 eval 函数中使用它们有单独的规则。

    let 和 const

    let 声明的标识符 const 具有块作用域 ,除非它们直接在全局上下文中声明,在这种情况下它们具有全局作用域。

    注意: let , const var 都是提升的 。这意味着它们定义的逻辑位置是其封闭范围(块或函数)的顶部。但是,使用 let const 在控制权通过源代码中的声明点之前无法读取或赋值。过渡期称为暂时死区。

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

    函数参数名称

    函数参数名称的作用域为函数主体。请注意,这有点复杂。声明为默认参数的函数覆盖 参数列表 ,而不是函数主体。

    函数声明

    函数声明在严格模式下具有块作用域,在非严格模式下具有函数作用域。注意:非严格模式是一套复杂的新兴规则,基于不同浏览器古怪的历史实现。

    命名函数表达式

    命名函数表达式的作用域限于其自身(例如,为了递归的目的)。

    全局对象上隐式定义的属性

    在非严格模式下,全局对象上隐式定义的属性具有全局作用域,因为全局对象位于作用域链的顶部。在严格模式下,这些是不允许的。

    评估

    eval 字符串中,使用声明的变量 var 将被放置在当前范围内,或者,如果 eval 间接使用,则作为全局对象的属性。

    示例

    以下将引发 ReferenceError,因为名称 x , y 、 和 z 在函数之外没有任何意义 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)

    y 抛出 ReferenceError z ,但对 不会 x ,因为 的可见性 x 不受块约束。定义 、 if , for while 等控制结构主体的块的行为类似。

    {
        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 ),因此 0 会打印整个整数。 4

    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

    在循环顶部 for 使用 let 作用域是循环主体:

    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

    var , let 声明的变量 const 均作用于模块:

    // 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

    let 并且 const 在全局上下文中不向全局对象添加属性,但仍然具有全局范围:

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

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

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

    Catch 块参数的作用域是 catch 块主体:

    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

    其内部工作原理

    范围被定义为 标识符有效的代码 词汇

    在 JavaScript 中,每个函数对象都有一个隐藏的 [[Environment]] 对其创建时的 执行上下文 词法环境 的引用

    调用函数时, [[Call]] 会调用隐藏方法。此方法会创建一个新的执行上下文,并在新的执行上下文和函数对象的词法环境之间建立链接。它通过将 [[Environment]] 函数对象上的值复制到 新执行上下文的词法环境上的 外部引用

    请注意,新执行上下文和函数对象的词法环境之间的这种联系称为 闭包 .

    因此,在 JavaScript 中,作用域是通过由外部引用链接在一起的词法环境来实现的。这个词法环境链称为作用域链,标识符解析是通过在 链中向上搜索 匹配的标识符来实现的。

    了解 更多 .

返回
作者最近主题: