作用域 变量/函数提升

关于作用域的问题,在【ES6语法】、【作用域、this上下文、闭包】文中,均有涉及,今天来做一个详细的介绍。

在之前的文章中,提到作用域是可以静态分析出来的,因此作用域在定义时,就已经确定了。这是为了提高程序的可靠性,也减少了命名冲突。

作用域是定义变量和访问变量的范围。 

在ES5中,只有全局作用域函数作用域

全局作用域和全局对象 

全局作用域在页面打开时被创建,页面关闭时销毁。

全局作用域中有一个全局对象window,这是BOM中的浏览器窗口对象,由浏览器创建,可以直接使用。

当我们只做变量赋值,而不做变量声明时,该变量会被挂载到window对象上。举例:

var testNumber = 1
console.log(testNumber) //1
console.log(window.testNumber) //1const testNumber1 = 2
console.log(testNumber1) //2
console.log(window.testNumber1) //undefinedlet testNumber2 = 3
console.log(testNumber2) //3
console.log(window.testNumber2) //undefinedtestStr = 'str'
console.log(testStr) //str
console.log(window.testStr) //str

可以看到使用var声明,以及不做变量声明只赋值,该变量都会被挂载到window对象上。

ES6后出现const和let关键字,声明变量不会被挂载到window上。

作用域链

作用域链是变量和函数的可访问性和查找规则。 

function outer() {const outerVar = 'Outer variable';function inner() {const innerVar = 'Inner variable';console.log(innerVar); // Inner variableconsole.log(outerVar); // Outer variableconsole.log(globalVar); // Global variable}inner();
}const globalVar = 'Global variable';
outer();

从上述代码可以看到,内部函数可以访问自身函数体内的变量,又可以访问外部函数体的变量,还能访问全局变量。作用域链会从内层,到外层,逐层查找。

变量作用域

变量根据使用范围,可以分为:全局变量、局部变量。

全局作用域中声明的变量称为全局变量,该变量一般是在函数外部,不被函数体或者块级作用域包裹。全局变量的特点是在全局任何地方都可以使用。

全局变量有三种创建方式:

(1)使用var/let/const在全局范围内创建变量

let x = 2
const y = 3
var z = 4function getNum(){console.log(x)console.log(y)console.log(z)
}
getNum()

(2)只做变量赋值,不做声明

testStr = 'str'

如上,testStr变量会被挂载到window全局对象上,此时也可以在全局范围内使用该变量。

PS:就算是在函数体内赋值,不做变量声明,也会被挂载到全局对象上。如下:

function getNum(){str = 'str'
}
getNum()
console.log(str) // 'str'
console.log(window.str) // 'str'

(3)直接在全局对象上声明变量

window.num = 1
console.log(num) // 1
console.log(window.num) // 1

在ES6之前,声明在函数体内的变量,被称为局部变量。在ES6后,声明在块级作用域内的变量也被称为局部变量。函数的形参,也属于局部变量。

(1)声明在函数体内

function getNum() {var num = 2let sum = 0;const str = '1'
}
console.log(num, sum, str)

此时不管变量是如何声明,在外部打印都会报错

(2)声明在块级作用域内

{var x = 1let y = 2
}
console.log(x) // 1
console.log(y) //y is not defined

可以看到ES6以前没有let/const关键字,不会产生块级作用域,使用var无法产生局部变量。

使用let/const后,在块级作用域外部,无法访问局部变量。

全局变量与局部变量执行效率对比

  • 全局变量:浏览器关闭时,变量才会被销毁,比较占用内存
  • 局部变量:当其所在代码块运行结束后,就会被销毁,比较节约内存

全局变量局部变量重名,局部变量会屏蔽全局变量,以局部变量为准。

变量提升

JS在解析代码前,会有预处理阶段,将当前JS代码中所有变量定义和函数定义,放到所有代码的最前面。这就是变量提升的由来。

(1)使用var声明的变量会存在变量提升的问题,如下: 

console.log(str) // undefined
var str = 'str'

这里输出undefined并没有报错,说明存在变量提升。

(2)如果不进行变量声明,则不会产生变量提升的问题。如下:

console.log(a); //a is not defined
a = 123; //此时a相当于window.a

可以看到已经报错了,只有用var声明的变量才会存在变量提升。

 (3)在局部变量也存在变量提升,如下:

foo();function foo() {if (false) {var i = 123;}console.log(i); //undefined
}

即便if中的语句不被指向,也会被提前声明

函数提升

从上述代码可以看出,函数声明也存在函数提升,函数提升是默认存在的,目的是为了方便开发者,可以随意放置函数的位置。

函数表达式

fun1() //fun1 is not a function
var fun1 = function() {console.log('fun1执行')
}fun2() //Cannot access 'fun2' before initialization
const fun2 = function () {console.log('fun2执行')
}

从上述代码可以看到,函数表达式不存在提升问题,不过这两个报错不同。

函数提升优先级高于变量提升

fun() //'B'//变量提升
var fun = function () {console.log('A')
}
//函数提升
function fun() {console.log('B')
}
fun() //'A'

这个问题,在上一个代码块中做了说明,实际上函数表达式不存在提升的问题,因此用函数表达式声明的fun一开始并没有被执行。 

而函数存在提升,因此第一个fun()执行的是存在函数提升的函数体。

而变量赋值会导致函数被覆盖,可以看到变量赋值可以改变函数体。【函数表达式可以覆盖函数】

自从ES6出现let和const后,就不存在这个问题了,因为重名变量会报错,Identifier 'fun' has already been declared 

再来看一下这个例子:

var str = '1';function foo() {console.log(str); // 1console.log(window.str) // 1str = 2; 
}foo();
console.log(str); //2
console.log(window.str) // 2

上述代码中,str作为全局变量,使用var声明后被挂载到window上,在局部作用域中仍然可以被使用,并且可以被改变。

PS:定义形参就相当于在函数作用域中声明了变量。如下:

function fun(data){console.log(data)
}
fun()    //undefined
fun(123)    //123

块级作用域

在其他编程语言中(如Java、C#等),存在块级作用域,使用{}大括号包起来。如在Java中,if语句中声明的变量,只能在if语句中使用:

if(true){int num = 123;system.out.print(num); // 123
}
system.out.print(num); // 报错

但是在ES5中并不存在块级作用域的概念。直到ES6中出现let和const关键字,由这两个关键字声明的变量,存在块级作用域。

if(true) {var str = 'str'console.log(str) //'str'
}
console.log(str) //'str'

从上述代码可以看到,不在if的范围内,仍然可以使用该函数体内声明的变量。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/68066.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu20.04 运行 PL-VIO

文章目录 运行后不知为何没有线特征 运行后不知为何没有线特征

C#与AI的共同发展

C#与人工智能(AI)的共同发展反映了编程语言随着技术进步而演变,以适应新的挑战和需要。自2000年微软推出C#以来,这门语言经历了多次迭代,不仅成为了.NET平台的主要编程语言之一,还逐渐成为构建各种类型应用程序的强大工具。随着时…

什么情况该换手机?先看后买不踩坑

现在的智能手机发展的非常快,很多刚出来的1000多元的手机性能已经可以流畅玩游戏、刷视频了,而且基本上也能使用3-5年的时。如果真要把手机用到实在不能用了,可能真的会影响生活体验,还有可能因为电池鼓包等问题发生危险&#xff…

初步理解数据结构

引言 数据结构是计算机科学中的核心概念之一,它是存储、组织和管理数据的方式,直接影响算法的效率和程序的性能。无论是开发一个简单的应用程序,还是设计一个复杂的系统,选择合适的数据结构都是至关重要的。本文将深入探讨常见的…

centos操作系统上以service形式运行blackbox_exporter监控网页端口

文章目录 前言一、blackbox_exporter是什么二、使用步骤1.获取二进制文件2.准备部署脚本3.执行命令,进行部署4.prometheus中增加需要监控页面的job信息 三、查看部署结果四、配置到grafana中总结 前言 记录一下centos操作系统上以简单的service形式运行blackbox_ex…

26考研资料分享 百度网盘

基础班: 通过网盘分享的文件:2026【考研数学】等3个文件 链接:https://pan.baidu.com/s/1djzJiut1h0DH8WmrI05YHg?pwd1234 提取码:1234--来自百度网盘超级会员v3的分享 通过网盘分享的文件:01、2026【考研政治】 链接:https://pan.baidu.…

使用github提交Pull Request的完整流程

文章目录 1.Fork仓库2. git clone 仓库在本地3.对项目进行修改开发4.上传项目到远程仓库操作补充1. git add .2. git commit -m "提交信息"3. git pull4. git push总结完整工作流程示例 5.将更新的项目pull Request给原来的仓库主人 当多人进行项目的开发的时候&…

python编写Socket程序

文章目录 编写非阻塞的TCP连接程序编写UDP的socket程序创建连接发送数据 多线程管理udp 编写非阻塞的TCP连接程序 下面代码使用了select模块来管理多个 socket 连接,server_socket.setblocking(0)将服务器 socket 设置为非阻塞模式 ,在接收数据时&#…

在Ubuntu上使用Apache+MariaDB安装部署Nextcloud并修改默认存储路径

一、前言 Nextcloud 是一款开源的私有云存储解决方案,允许用户轻松搭建自己的云服务。它不仅支持文件存储和共享,还提供了日历、联系人、任务管理、笔记等丰富的功能。本文将详细介绍如何在 Ubuntu 22.04 LTS 上使用 Apache 和 MariaDB 安装部署 Nextcl…

PHP礼品兑换系统小程序

🎁 礼品兑换系统:革新企业礼品管理,专属神器来袭! 💻 一款专为追求高效与个性化的现代企业量身打造的礼品兑换系统,它基于强大的ThinkPHP框架与前沿的Uniapp技术栈深度融合,不仅完美适配礼品卡…

数据结构基础之《(16)—链表题目》

一、链表问题 1、对于笔试,不用太在乎空间复杂度,一切为了时间复杂度 2、对于面试,时间复杂度依然放在第一位,但是一定要找到空间最省的方法 二、快慢指针 逻辑: 慢指针一次走1步 快指针一次走2步 当快指针走完的时…

OpenHarmonyOS 3.2 编译生成的hap和app文件的名称如何配置追加版本号?

找了一圈发现官方的文档都是最新的,3.2很多API都不支持,比如获取OhosAppContext,通过OhosAppContext来获取应用版本号,最终是通过读取app.json5的文件内容来读取版本号,最终修改entry下的hvigorfile.ts如下&#xff0c…

mapbox加载geojson,鼠标移入改变颜色,设置样式以及vue中的使用

全国地图json数据下载地址 目录 html加载全部代码 方式一:使用html方式加载geojson 1. 初始化地图 2. 加载geojson数据 设置geojson图层样式,设置type加载数据类型 设置线条 鼠标移入改变颜色,设置图层属性,此处是fill-extru…

Langchain+讯飞星火大模型Spark Max调用

1、安装langchain #安装langchain环境 pip install langchain0.3.3 openai -i https://mirrors.aliyun.com/pypi/simple #灵积模型服务 pip install dashscope -i https://mirrors.aliyun.com/pypi/simple #安装第三方集成,就是各种大语言模型 pip install langchain-comm…

【kong gateway】5分钟快速上手kong gateway

kong gateway的请求响应示意图 安装 下载对应的docker 镜像 可以直接使用docker pull命令拉取,也可以从以下地址下载:kong gateway 3.9.0.0 docker 镜像 https://download.csdn.net/download/zhangshenglu1/90307400, postgres-13.tar http…

高效查找:二分查找算法解析

1.二分查找简介 二分查找算法(Binary Search)是一种高效的查找算法,适用于有序数组或序列。它的基本思想是通过逐步缩小查找范围,将查找区间一分为二,直到找到目标值或确定目标值不存在。 算法原理:在数组…

数据统计–图形报表(day11)

Apache ECharts 介绍 Apache ECharts 介绍 Apache ECharts 是一款基于 Javascript 的数据可视化图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表。 官网地址:Apache ECharts 入门案例 Apache Echarts官方…

Docker可视化管理工具Portainer

Portainer简介 Portainer 是一个轻量级的、开源的容器管理工具,提供了一个直观的 Web 用户界面(UI),用于管理 Docker 和 Kubernetes 环境。它简化了容器的部署、监控和管理,特别适合不熟悉命令行操作的用户或团队。 …

C++入门14——set与map的使用

在本专栏的往期文章中,我们已经学习了STL的部分容器,如vector、list、stack、queue等,这些容器统称为序列式容器,因为其底层是线性序列的数据结构,里面存储的是元素本身。而本篇文章我们要来认识一下关联式容器。 &am…

浅析云场景SSD实时迁移技术

在数据中心的运营管理中,负载均衡和系统容错是确保高效稳定运行的关键。SSD实时迁移技术,为解决这些问题提供了创新方案,成为数据中心技术发展的重要驱动力。 以AI训练任务为例,其运行时间长且无需用户频繁交互。数据中心的负载会…