JavaScript 作用域scope简单汇总
作者:得闲读书 发布时间:2024-04-18 10:54:55
什么是作用域
程序的执行,离不开作用域,也必须在作用域中才能将代码正确的执行。
所以作用域到底是什么,通俗的说,可以这样理解:作用域就是定义变量的位置,是变量和函数的可访问范围,控制着变量和函数的可见性和生命周期。
而JavaScript中的作用域,在ES6之前和ES6之后,有两种不同的情况。
ES6之前,JavaScript作用域有两种:函数作用域和全局作用域。
ES6之后,JavaScript新增了块级作用域。
作用域的特性
在JavaScript变量提升的讨论中,我们其实是缺少了一个作用域的概念的,变量提升其实也是针对在同一作用域中的代码来说的。
对编译器的了解,让我们明白,对于一段代码【var a = 10】变量的赋值操作,其实是包含了两个过程:
1、变量的声明和隐式赋值(var a = undefined),这个阶段在编译时
2、变量的赋值(a = 10),这个阶段在运行时
先看一下如下代码:
var flag = true;
if(flag) {
var someStr = 'flag is true';
}
function doSomething() {
var someStr = 'in doSomething';
var otherStr = 'some other string';
console.log(someStr);
console.log(flag);
}
doSomething();
for(var i = 0; i < 10; i++) {
console.log(i);
}
console.log(i);
{
var place = 'i do not want to be visited';
}
那么这一些代码在编译之后,执行之前,根据变量提升的机制,我们可以知道应该是下面这个样子:
function doSomething() { // 函数优先提升
// 提升隐式赋值
var someStr = undefined;
var otherStr = undefined;
someStr = 'in doSomething';
otherStr = 'some other string';
console.log(someStr);
console.log(flag);
}
// 隐式赋值和提升
var flag = undefined;
var someStr = undefined;
var i = undefined;
var place = undefined;
flag = true;
if(flag) {
someStr = 'flag is true';
}
for(i = 0; i < 10; i++) {
console.log(i);
}
doSomething();
console.log(i);
{
place = 'i do not want to be visited';
}
因为变量的提升特性,以及无块级作用域的概念,所以代码中在同一个作用域中变量和函数的定义,在编译阶段都会提升到顶部。
通过上述代码,我们大体上可以得出作用域的特性:
第一、内部作用域和外部作用域是嵌套关系。外部作用域完全包含内部作用域。
第二、内部作用域可访问外部作用域的变量,但是外部作用域不能访问内部作用域的变量,(链式继承,向上部作用域查找)。
第三、变量提升是在同一个作用域内部出现的。
第四、作用域用于编译器在编译代码时候,确定变量和函数声明的位置。
块级作用域
上述代码,在ES6+的环境中运行,也是和ES6之前是相同的结果,但是ES6不是引用了块级作用域吗,为什么大括号块内的代码还是会出现和之前一样的编译方式呢?
那么,ES6中的块级作用域到底是什么?
let & const
利用var定义的变量,具有提升的性质,可能会影响代码的执行结果。
这是var定义变量的缺陷,那么如何规避这种缺陷呢?在ES6中,设计出来了let和const来重新定变量。
但是,由于JavaScript标准定义的非常早,1995年5月JavaScript方案定义,1996年微软提供了JavaScript解决方案JScript。而网景公司为了同微软竞争,神情了JavaScript标准,于是,1997年6月第一个国际标准ECMA-262便颁布了。
C语言标准化的过程却是将近二十年后才颁布。
所以,我们以后设计的语言既要兼容var也要有自己的块级作用域,让var和let以及const在引擎做到兼容。
所以,我们定义块级作用域的标准,只能从定义变量的方式入手,而不是直接一个{}块就可以解决。
先让我们看一下下面代码:
var name = 'someName';
function doSomething(){
console.log(name);
if(true) {
var name = 'otherName';
}
}
doSomething();
结果:undefined
产生这个结果的原因是我们函数内部的变量提升,覆盖了外部作用域的变量,也就是说,其实打印出来的值是doSomething函数中的变量声明的值。
但是这样却并不符合块级作用域的预期,如果有许多类似代码,理解起来也会相当困难。如果将代码用ES6方式改写:
let name = 'someName';
function doSomething(){
console.log(name);
if(true) {
let name = 'otherName';
}
}
doSomething();
结果:'someName'
从运行结果看,我们真正的做到了块级作用域应该有的效果,那么let和const又是如何支持块作用域的呢?
执行上下文
先想想一下JavaScript中的一个作用域两个执行上下文中的编译过程中的环境:
变量环境:编译阶段var声明存放的位置(一个大对象)。
词法环境:我们代码书写的位置,也是let和const的初始化位置(代码按词法环境顺序执行,按照{}划分的栈结构)。
而在编译阶段,我们将var定义的变量全都在编译过程在变量环境初始化为undefined,但是用let和const定义的变量,其实他们并未在变量环境初始化,而是在词法环境初始化,也就是执行代码位置初始化。
词法环境的特点:按照{}划分的一个栈结构。
变量查找方式
JavaScript中变量查找的方式:沿着词法环境的栈顶向下查找,找不到的变量去变量环境中查找,这样就形成了先查找代码块中的变量,再查找提升之后的变量环境,这样就形成了块级作用域的概念。
上面的代码形成两种环境的情况如下:
一、全局环境的执行上下文
变量环境:函数声明function doSomething() { … }
词法环境栈:执行到let name = ‘someName';让语句name = ‘someName'入栈。
二、doSomething的执行上下文(被全局环境包裹)
变量环境:无
词法环境栈情况:执行到let name = ‘otherName',语句的时候,name = ‘other'才会入栈;
JavaScript代码执行方式
执行doSomething的时候,还未执行let name = ‘otherName',所以,此时doSomething的词法环境中并未有name = ‘otherName',这个时候查找,只能向外部作用域查找(全局作用域)
此时查找到全局作用域name = ‘someName'所以此时就打印了someName
代码接着执行到了if语句内部,才会将name = ‘otherName'入栈,但是此时因为语句已经执行完毕,所以也就无关痛痒了。
JavaScript也就通过这种方式,实现了块级别作用域。
总结
JavaScript中的作用域总的来说,分为块级作用域、函数作用域、全局作用域。
而每个作用域都会创建自身的执行上下文,每一个执行上下文又分为了变量环境和词法环境两部分。
块级作用域的实现,其实是根据定义的let和const声明的变量放置在词法环境栈中这一特性来实现。
这一特性被社区的人叫做‘暂时性死区',但是在JavaScript标准中并未有这个概念。
只有理解了作用域的概念,才能真正明白JavaScript的执行机制,才能减少我们因为变量定义等发生的错误。
来源:http://www.gaoyunjiao.fun/?p=148


猜你喜欢
- 将.py文件转化为.exe文件首先需要第三方库 pyinstaller1.如果没有安装pyinstaller,则在命令提示符输入 pip i
- 今天在调试项目的时候出现下面的错误信息:SoapFaultlooks like we got no XML document (D:\php
- 一、流程分析:1.用户在项目前端,输入手机号,然后点击【获取验证码】,将手机号发到post到后台。2.后台验证手机号是否合法,是否已被占用,
- 这里有numpy数组的相关介绍https://www.jb51.net/article/130657.htm排序numpy与python列表
- FTP即文件传输协议;它基于客户机-服务器模型体系结构,应用广泛。它有两个通道:一个命令通道和一个数据通道。命令通道用于控制通信,数据通道用
- 前言pytest.mark.skip可以标记无法在某些平台上运行的测试功能,或者您希望失败的测试功能希望满足某些条件才执行某些测试用例,否则
- 下策——查询出结果后将时间排序后取第一条select * from a where create_time<="2017-0
- (一)什么是json:JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写。同时也
- 本文为大家分享了mysql 8.0.12 解压版安装教程,供大家参考,具体内容如下1、首先在官网上下载mysql8.0.12的压缩包:下载地
- 如果你有一字段dm记录了一个url,为了更好的优化模糊查询速度或统计速度,在数据表原有的结构上增加3个字段,分别为 `sdm`
- 问题详情:偶然在根目录创建文件夹的时候,突然显示错误,当时很惊讶,以前没遇见过这样的问题。当时界面是这样的。 用了一个 cd / 命令从用户
- 前言centos 是自带python的。但是版本稍微旧一些。搞python开发,肯定要用新一点的稳定版。所以,要升级一下python。本文将
- 数据备份与还原第三篇,具体如下基础概念:备份,将当前已有的数据或记录另存一份;还原,将数据恢复到备份时的状态。为什么要进行数据的备份与还原?
- 本文实例讲述了Python基于回溯法子集树模板解决数字组合问题。分享给大家供大家参考,具体如下:问题找出从自然数1、2、3、...、n中任取
- 正则表达式简介正则表达式,又称正规表示式、正规表示法、正规表达式、规则表达式、常规表示法(英语:Regular Expression,在代码
- 今天我们将介绍处理大量数据时非常方便的工具。我不会只告诉您可能在手册中找到的一般信息,而是分享一些我发现的小技巧,例如tqdm与 multi
- 一、功能目标用户输入一个类似 1-2*((60-30+(-40/5)*(9-2*5/3+7/3*99/4*2998+10*568
- 最近项目中的资产的任务状态频频出现问题,查看日志文件,看代码逻辑,也没发现什么具体的错误,总是过段时间就会出现一个表的字段没有更新的问题,很
- 1. getattr()函数是Python自省的核心函数,具体使用大体如下:class A: def __init__(self): sel
- 最小编辑距离或莱文斯坦距离(Levenshtein),指由字符串A转化为字符串B的最小编辑次数。允许的编辑操作有:删除,插入,替换。具体内容