关于作用域与作用域链

简介: 继续来了解一下ES6,顺便把前段时间留下作用域的坑也填上。JavaScript中,有一个被称之为作用域(scope)的特性,在之前闭包的文章中提到过,现在来梳理一下。

作用域与作用域链

前言

继续来了解一下ES6,顺便把前段时间留下作用域的坑也填上。

JavaScript中,有一个被称之为作用域(scope)的特性,在之前闭包的文章中提到过,现在来梳理一下。

作用域

作用域是指在程序中定义变量的区域,该位置决定了变量的生命周期。通俗地理解,作用域就是变量与函数的可访问范围,即作用域控制着变量和函数的可见性和生命周期。

ES6之前,JavaScript中有两种作用域,分别叫做全局作用域函数作用域

  • 全局作用域:全局作用域中的对象在代码中的任何地方都能访问,其生命周期伴随着页面的生命周期,页面被关闭才会被销毁。
  • 函数作用域:函数作用域就是在函数内部定义的变量或者函数,并且定义的变量或者函数只能在函数内部被访问。函数执行结束之后,函数内部定义的变量会被销毁。

作用域给我的最大感觉就是隔离变量,在不同作用域的同名变量是不会冲突的。

在ES6中,提出了块级作用域的概念。

  • 块级作用域的特点:在代码块内部定义的变量在外部是访问不到的,并且变量会在该代码块执行完后被销毁掉。
  • 块级作用域形式:就是使用大括号包裹的代码块{},比如函数,判断语句,循环语句,甚至单独的一个{},包裹的也可以看作是一个块级作用域。

    // if块
    if(){}
    // while 块
    while(){}
    // 函数块
    function fn(){}
    // for循环块
    for(let i = 0; i < 10; i++){}
    // 单独的{}
    {}
  • 如何让块级作用域生效,ES6给我们提供了两个关键字:let和const

    来看看例子吧

    for(var i = 0; i < 100; i++){
    }
    console.log(i); // 100
    for(let i = 0; i < 100; i++){
    }
    console.log(i); // not defined

    我们来思考一道题:每隔一秒钟,打印出来一个自然数,自然数递增。

    是不是用一个for循环,里面设置好一个定时器?

    是这样?

    for(let i = 0; i<10; i++){
        setTimeout(function(){
            console.log(i)
        },1000*i)    
    }

    还是这样?

    for(var i = 0; i<10; i++){
        setTimeout(function(){
            console.log(i)
        },1000*i)    
    }

let、const关键字解决var变量提升的问题

之前有说过变量提升的问题,现在来看一下let与const是怎么解决问题的。

首先举一下都会产生什么问题:

  • 变量提升会导致变量值被覆盖

    var a = "张三"
    function showA(){
        console.log(a);
        if(0){
            var a = "李四"
        }
        console.log(a);
    }
    showA() // undefined  undefined
  • 该销毁的变量销毁不掉

    for(var i = 0; i<10; i++){
    }
    console.log(i)

那么let、const是如何解决问题的呢?

先来看一下这段代码

function fun(){ 
    var a = 11;
    let b = 22;
    {     
        let b = 33;
        var c = 44;
        let d = 55;
        console.log(a); // 11
        console.log(b); // 33
    }
    console.log(b); // 22
    console.log(c); // 44
    console.log(d); // not defined
} 
fun()
  • let与const关键字创建的变量是存储在词法环境的,而var创建的变量是存储在变量环境中的,访问变量的时候,先从执行上下文的词法环境中查找,再到变量环境中进行查找。
  • 当块级内部代码执行结束后,内部let与const创建的变量会被销毁。
  • let跟const创建的变量,初始化不提升,创建提升,所以会造成暂时性死区

    来看一下什么是暂时性死区吧

    console.log(a);
    let a = 1;

    这个时候,我们的浏览器报错与之前的not defined就不一样了,它会报 Cannot access 'a' before initialization这个错误,告诉我们在初始化前不能访问a这个变量,这是因为let与const只有创建提升,没有初始化提升,所以造成了这个问题。

再次说明一下变量提升的问题

  • var的创建和初始化被提升,赋值不会被提升
  • let的创建被提升,初始化和赋值不会被提升,所以会有暂时性死区的问题(就是访问不到变量)
  • function的创建、初始化、赋值都会被提升

作用域的特点:是代码编译阶段就决定好的,和函数是如何调用的没有关系

看下面这段代码:

function biqibao() { 
    var myName = "海绵宝宝";
    let test1 = 100;
    if (1) { 
        let myName = "派大星"; 
        console.log(test);
    }
}
function fn() { 
    var myName = "章鱼哥";
    let test = 2;
    { 
        let test = 3;
        biqibao();
    }
};
var myName = "珊迪";
let myAge = 10;
let test =1;
fn(); // 1

解释一下流程,biqibao函数在fn函数中被调用,要打印test变量,首先查找biqibao函数的内部作用域,没有找到,于是去到全局作用域进行查找,找到了let test = 1这句话,于是,打印1。与fn函数中的let test = 2这句话,一点关系都没有。

这就是在诠释上面作用域特点的那句话。

作用域链

作用域链:当一个函数中使用了某个变量,首先会在自己内部作用域查找,然后再向外部一层一层查找,直到全局作用域,这个链式查找就是作用域链

let num = 1;
function fn1(){
    function fn2(){
        function fn3(){
            console.log(num);
        }
        fn3();
    }
    fn2();
}
fn1(); // 1

fn3要打印num变量,于是他从fn3的函数作用域,找到fn2的函数作用域,再找到fn1的函数作用域,再找到全局作用域,终于找到了num变量,成功将其打印。

相关文章
|
存储 安全 数据管理
新型数据库技术:基于区块链的分布式数据存储系统
传统数据库系统面临着中心化管理、数据安全性和可信度等方面的挑战。本文介绍了一种基于区块链技术的新型数据库系统,通过分布式存储和去中心化的特性,提高了数据的安全性和可信度,同时实现了高效的数据管理和共享。该系统在多个领域如金融、医疗和物联网等具有广阔的应用前景。
|
前端开发 JavaScript
JS中Promise详解
JS中Promise详解
217 0
|
1月前
|
人工智能 JavaScript 前端开发
JavaScript 中 `apply`、`call` 和 `bind` 的具体理解与区别
`apply`、`call` 和 `bind` 是 JavaScript 中用于改变函数 `this` 指向的方法。`apply` 以数组形式传递参数并立即执行函数;`call` 则以逗号分隔的参数列表传递并立即执行;而 `bind` 不会立即执行,而是返回一个绑定好 `this` 和部分参数的新函数,适用于延迟调用。三者在参数传递方式和执行时机上有所不同,适用于不同的开发场景,如动态绑定上下文、参数不定的函数调用、事件处理等。掌握它们的使用可以提升代码灵活性与复用性。
192 0
|
12月前
|
JavaScript
typeScript基础(3)_ts函数默认值和可选参数
本文介绍了在TypeScript中如何使用函数的默认值和可选参数。展示了如何为函数参数指定默认值,使得在调用函数时可以省略某些参数,以及如何定义可选参数。
842 2
|
11月前
|
前端开发 JavaScript CDN
React 安装(CDN)
10月更文挑战第6天
285 2
|
JavaScript 前端开发 搜索推荐
Vue 路由的hash模式和history模式有什么区别?
在Vue.js框架中,路由管理是单页面应用(SPA)不可或缺的功能。Vue 路由提供了两种模式:hash模式和history模式,这两种模式主要负责处理URL的变更而无需重新加载整个页面,实现前端路由的功能。
510 19
|
弹性计算 人工智能 运维
60分钟深度测评阿里云基于大模型构建的操作系统智能助手
OS Copilot 概要 OS Copilot 是阿里巴巴云针对Linux操作系统开发的智能助手,集成在Alibaba Cloud Linux中,利用大模型技术提供自然语言问答、命令行辅助、阿里云CLI调用和系统运维功能。它尤其适合新手,直观的交互方式提升效率。此外,OS Copilot支持在操作系统内直接管理阿里云资源,简化运维任务。目前,该助手仅在特定版本的Alibaba Cloud Linux上可用。体验者可以通过提供的链接和指南进行实操,体验其功能,如命令行的自然语言交互和环境变量配置。OS Copilot在提高用户体验和工作流集成方面的创新,预示着未来AI在操作系统中的广泛应用。
|
安全 Linux 数据处理
探索Linux的kmod命令:管理内核模块的利器
`kmod`是Linux下管理内核模块的工具,用于加载、卸载和管理模块及其依赖。使用`kmod load`来加载模块,`kmod remove`卸载模块,`kmod list`查看已加载模块,`kmod alias`显示模块别名。注意需有root权限,且要考虑依赖关系和版本兼容性。最佳实践包括备份、查阅文档和使用额外的管理工具。
|
前端开发 JavaScript Go
webpack -vite(Rollup )-Gulp (一)
webpack -vite(Rollup )-Gulp (一)
234 0
|
Ubuntu 搜索推荐 Linux
【专栏】8款适合学生的Linux发行版,看看有没有你喜欢的!
【4月更文挑战第28天】本文介绍了8款适合学生的Linux发行版:Ubuntu(用户友好,稳定且有教育资源)、Linux Mint(优化用户体验)、Fedora(创新前沿)、openSUSE(强大稳定)、Elementary OS(简洁设计)、Manjaro(Arch Linux的易用版)、Zorin OS(类似Windows)和Kubuntu(KDE桌面环境)。选择时需考虑易用性、软件资源、社区支持和稳定性。这些发行版各具特色,适合不同需求的学生,有助于提升技术能力和探索精神。建议学生亲自尝试,找到最适合自己的Linux发行版,以适应不断发展的技术环境。
467 0

热门文章

最新文章