JavaScript作用域

一、什么是作用域?

作用域指一个变量的作用范围

每个作用域都是一个独立的地盘,目的就是为了保证当前作用域内的变量不会外泄,且不会和其他作用域中的同名变量冲突。

在JavaScript中的作用域类型

  • 全局作用域
  • 函数作用域(局部作用域)
  • 块级作用域(let/const  ES6新增

二、作用域详解

2.1 全局作用域

写在<script>标签内的最外层变量和函数都是全局作用域。当页面打开时全局作用域会自动创建,而当页面关闭时就会销毁。所有全局作用域对象都可以通过window对象的属性进行访问。

//示例:
<script>console.log(window);var a = 1;function aa(){console.log("aa")
}var person = {name:"张三"};window.aa();
console.log(window.a);
console.log(window.person.name);</script>

2.2 函数作用域

· 定义于函数内部的变量或函数。也称为局部作用域。在函数调用时被创建,函数执行完毕后自动销毁。函数每调用一次都会创建一个新的函数作用域,他们之间是相互独立的。

· 函数作用域中可以访问全局作用域的变量和函数,反之则不行。

· 在函数内部访问一个变量或函数时,会现在当前作用域中查找。如果没找到,就到上一级的作用域中查找,直到全局作用域。

· 函数内部也相当于一个小的全局作用域,所以定义在函数内部的变量和函数也存在变量提升。

//示例:
<script>var name = "张三";
function aa(){var name = "李四";console.log(name) //李四console.log(window.name) //张三
}
aa();</script>

2.3 块级作用域(ES6)

带“{}”的基本都为块级作用域,如:if(){ }、for(){ }、catch(err){}。var和function没有块级作用域的概念,只有let和const有块级作用域概念。

//示例:
<script>if(true){let a = 10;console.log(a) //10
}    for(let i=0;i<=0;i++){console.log(i) //0
}console.log(a) //ReferenceError: a is not defined
console.log(i) //ReferenceError: i is not defined</script>

三、什么是作用域链?

      当一个块或函数嵌套在另一个块或函数中时,就发生了作用域嵌套。因此,在当前作用域中没有找到某个变量时,引擎就会在外层嵌套的作用域中继续查询,直到找到该变量。如果抵达最外层的作用域(全局作用域)还找不到,就会抛出ReferenceError异常。

四、 作用域原理

1.1 编译过程

  作用域的内部原理分为5个阶段

  • 编译阶段编译器将代码分解成词法单元并将词法单元解析成一个数结构AST
  • 执行阶段编译器询问作用域中是否存在某变量,不存在就创建,并为引擎生成运行时所需要的代码
  • 查询阶段引擎询问作用域中是否存在某变量,如果存在引擎就会使用该变量并为其赋值,不存在就继续向上查找
  • 嵌套阶段当前作用域不存在某变量会继续向上查询
  • 异常阶段直到顶层作用域window也没有找到某变量,就抛出异常:ReferenceError

例:var a = 2;的执行过程

首先引擎会认为这里有两个完成不同的声明,一个由编译器在编译时处理(var a),另一个则由引擎在运行时处理( = 2)。

  • 编译器会进行两步操作:
      语法分析阶段:
         1>将这段代码分解成词法单元
         2>将词法单元解析成一个数结构。

      代码执行阶段:
         1>遇到var a,编译器会询问作用域是否已经有一个名称为a的变量存在于同一作用域的集合中。如果是,编译器会忽略该声明继续进行编译。否则它会要求作用域在当前作用域的集合中声明一个新的变量,命名为a。
          2>接下来编译器会为引擎生成运行时需要的代码,这些代码被用来处理a = 2这个赋值操作
  • 引擎运行时会首先询问作用域,在当前作用域集合中是否存在一个 叫做a的变量。如果存在,引擎就会使用该变量,如果否,引擎会继续在上一层作用域中查找。
  • 如果引擎最终找到了a变量,就会将2赋值给它。否则引擎就会举手示意并抛出一个异常。

总结:变量的赋值操作会执行两个动作,首先编译器会在当前作用域中声明一个变量(变量未声明过时),然后在运行时引擎会在作用域中查找该变量,如果能找到就给它赋值。  

1.2 详解LHS和RHS

 LHS和RHS的含义为“赋值操作的左侧或右侧”,但是并不意味着就是“=”赋值操作符的左侧或右侧。赋值操作还有其他几种形式,因此在概念上最好将其理解为:

  • LHS:(查询变量的容器本身,目的是:为某个变量赋值set)
  • RHS:(查询变量的源值,目的是:获取某个变量的值get)

例:

   console.log( a );   //这里的a没有赋予任何值,所以需要查找并取得a的值,所以是RHS查询。

   a = 2; //这里的a则是LHS查询,因为我们并不关心当前的值是什么,只是要为 = 2这个赋值操作找到一个目标。

总结:LHS 和 RHS 查询都会在当前执行作用域中开始,如果有需要(也就是说它们没有找到所 需的变量),就会向上级作用域继续查找目标变量,这样每次上升一级作用域,最后抵达全局作用域(顶层),无论找到或没找到都将停止。

        不成功的 RHS 引用会导致抛出 ReferenceError 异常。不成功的 LHS 引用会导致自动隐式 地创建一个全局变量(非严格模式下),该变量使用 LHS 引用的目标作为标识符,或者抛 出 ReferenceError 异常(严格模式下)

五、词法作用域

        词法作用域就是定义在词法阶段的作用域。是根据代码中变量和块作用域的位置所决定的,因此当词法分析器处理代码时会保持作用域不变(大部分情况,eval和with特殊)。

在下列示例中,编译器在词法(分词)阶段会创建三个不同的作用域,他们三个作用域是严格包含的

/*foo方法为全局作用域在foo这个函数作用域中又存在a、b、bar在bar函数作用域中存在c
*/function foo(a) {var b = a * 2;function bar(c) {console.log( a, b, c );}bar( b * 3 );
}
foo( 2 ); // 2, 4, 12

六、变量提升

         在作用域原理中,第一阶段也就是编译阶段有一部分工作就是找到所有的变量和函数在内的所有声明,并用合适的作用域将他们关联起来。这个处理工作是在代码执行阶段之前就处理完成的。

        在JavaScript中,var a=2;  js引擎会将其看成两个声明:var a和a=2;。第一个定义声明是在编译阶段进行的。第二个赋值声明会被留在原地等待执行阶段

       函数声明>变量声明 函数声明和变量声明都会被提升,但是函数会首先被提升。

七、立即执行函数(IIFE)

(function foo(){console.log("我被执行了")
})();//或者
(function(){ console.log("我被执行了")
}());//或者
(function IIFE(){ console.log("我被执行了")
})()。

        由于函数被包含在一对()括号内部,因此成为了一个表达式,通过在末尾加上另外一个()就可以立即执行这个函数,比如(function foo(){ ... })()。第一个()将函数变成表达式,第二个()执行了这个函数。

        IIFE用法1:把他们当作函数调用并传递参数进去。

<script>var a = 10;
(function IIFE(global){var a = 3;console.log(a) //3console.log(global.a) //10
})(window)</script>

        IIFE用法2: 倒置代码的运行顺序,将需要运行的函数放在第二位,在IIFE执行之后当作参数传递进去。

<script>/*函数表达式 def 定义在片段的第二部分,然后当作参数(这个参数也叫作 def)被传递进IIFE 函数定义的第一部分中。最后,参数 def(也就是传递进去的函数)被调用,并将window 传入当作 global 参数的值。
*/var a = 10;
(function IIFE(def){def(window)
})(function def(global){var a = 3;console.log(a) //3console.log(global.a) //2
})</script>

七、作用域闭包

        函数内部可以访问所以外部作用域中的变量,但是反之则不行,这就形成了闭包,闭包在我们的代码中其实很常见,比如在定时器、事件监听器、ajax请求或者任何其他的异步(或者同步)任务重,只要使用了回调函数,实际上就是在使用闭包。

延迟函数的回调会在循环结束时才执行。

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

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

相关文章

MongoDB——索引(单索引,复合索引,索引创建、使用)

MongoDB索引 官方文档 https://docs.mongodb.com/manual/indexes/#create-an-index 默认索引 _id index Mongodb 在 collection 创建时会默认建立一个基于_id 的唯一性索引作为 document 的 primarykey&#xff0c;这个 index 无法被删除 单个字段索引 单字段索引是 Mongo…

【Skynet 入门实战练习】开发环境搭建 | 运行第一个项目 | debug console 简单使用

文章目录 写在前面开发环境搭建skynet配置文件项目&#xff0c;启动&#xff01; debug console 写在前面 本系列【Skynet 入门实战练习】所有源码同步&#xff1a;https://gitee.com/Cauchy_AQ/skynet_practice 开发环境搭建 skynet skynet 框架地址&#xff1a;https://g…

什么款式的蓝牙耳机跑步不容易掉?推荐几款很不错的运动耳机

​如果你正在寻找一款性能卓越、佩戴舒适的耳机&#xff0c;那么运动耳机绝对是你的不二选择。它们不仅具备出色的音质&#xff0c;还具备防水、防汗、防震等多项特点&#xff0c;让你在运动时更加尽情享受音乐。接下来给大家推荐几款很不错的运动耳机。 1.南卡开放式运动耳机…

cmake 升级

您好&#xff0c;这是Bing。您的问题是如何解决cmake的版本过低的错误。我可以给您一些建议。请看以下的方法&#xff1a; 您可以通过apt命令来卸载当前的cmake版本&#xff0c;然后从官网下载最新的cmake安装包&#xff0c;解压并编译安装。具体的步骤如下1&#xff1a; 检查…

南京数字孪生赋能工业制造,加速推进制造业数字化转型

随着南京信息技术的迅猛发展和工业管理的不断演进&#xff0c;传统的工业管理方式已经无法满足企业对高效、智能和可持续发展的需求。针对这一情况&#xff0c;数字孪生技术应运而生&#xff0c;为南京工业管理带来了全新的变革和机遇。以数字孪生为理念&#xff0c;三维可视化…

闪存基本原理

系列文章目录 一、SSD主控 二、PCIe和NVMe控制器前端子系统 文章目录 系列文章目录三、闪存基本原理1.结构2.衍生问题 三、闪存基本原理 闪存是SSD的存储介质&#xff08;NAND Flash&#xff09;&#xff0c;它是一种非易失性存储器&#xff08;Non-volatile memory&#xff0…

centos7 怎么让命令行显示中文(英文->中文)

要让CentOS 7命令行显示中文&#xff0c;您需要确保您的系统支持中文字符集&#xff0c;并在命令行中设置正确的语言环境。以下是设置中文字符集和语言环境的步骤&#xff1a; 首先&#xff0c;确保您的系统已经安装了中文字体。在终端中运行以下命令来查看安装的中文字体&…

SDN核心技术与内容

NFV(Network Function Virtualization)--网络功能虚拟化 SDN典型架构&#xff1a;应用层、控制层、基础设施层 应用层--不同业务和应用控制层--处理数据平面资源的编排&#xff0c;维护网络拓扑、状态信息基础设施层--数据处理、转发、状态收集 南向接口--控制层与基础设施层接…

开源免费的流程设计器如何选型

大家在开发OA办公自动化、ERP、CRM、BPM、低代码平台等项目的时候&#xff0c;经常用到流程引擎&#xff0c;目前主流的开源流程引擎有activiti、flowable、camunda。这几个开源的流程引擎均基于BPMN2.0国际规范标准&#xff0c;其功能均比较强大&#xff0c;接口也很丰富。但涉…

关键性进展! 小米造车露真容 预计明年上市

大家好,我是极智视界,欢迎关注我的公众号,获取我的更多前沿科技分享 邀您加入我的知识星球「极智视界」,星球内有超多好玩的项目实战源码和资源下载,链接:https://t.zsxq.com/0aiNxERDq 小米在各种不同的产品上都在不断打上自己的品牌,这绝对不止于智能手机,而是有智能…

javaScript 内存管理

1 js 内存机制 内存空间&#xff1a;栈内存&#xff08;stack&#xff09;、堆内存&#xff08;heap&#xff09; 栈内存&#xff1a;所有原始数据类型都存储在栈内存中&#xff0c;如果删除一个栈原始数据&#xff0c;遵循先进后出&#xff1b;如下图&#xff1a;a 最先进栈&…

【剪枝】torch-pruning的基本使用

论文&#xff1a;DepGraph: Towards Any Structural Pruning 工程&#xff1a;https://github.com/VainF/Torch-Pruning 算法和库的使用介绍&#xff1a;CVPR 2023 | DepGraph 通用结构化剪枝 1 TP的简介 该算法介绍了DepGraph 如何建模结构化剪枝中的层依赖&#xff0c;实现任…

英国国家量子计算中心与IBM签署重要协议!英国进入实用量子时代

​&#xff08;图片来源&#xff1a;网络&#xff09; 近日&#xff0c;英国国家量子计算中心&#xff08;NQCC&#xff09;与IBM达成了一项重要协议。根据该协议&#xff0c;NQCC将为英国研究人员提供IBM量子高级计划的云访问权限&#xff0c;其中包括IBM的量子计算系统舰队。…

一文带你拿下MySQL之增删查改(基础)

✏️✏️✏️今天给各位带来的是关于数据库增删查改基础方面的知识。 清风的CSDN博客 &#x1f61b;&#x1f61b;&#x1f61b;希望我的文章能对你有所帮助&#xff0c;有不足的地方还请各位看官多多指教&#xff0c;大家一起学习交流&#xff01; 动动你们发财的小手&#xf…

Activiti7工作流引擎:生成实时的流程图片

实时获取当前流程对应的流程图片&#xff0c;并对当前正在审批的节点进行高亮显示。 public class ActivitiController {Autowiredprivate ProcessEngine processEngine;Autowiredprivate RepositoryService repositoryService;Autowiredprivate RuntimeService runtimeService…

vue超好用的自定义指令封装

一、指令封装 目录结构&#xff1a; index.ts 统一注册 import { App, Directive } from "vue"; import auth from "./modules/auth"; import copy from "./modules/copy"; import waterMarker from "./modules/waterMarker"; impor…

OpenStack-train版安装之基础组件安装

基础组件安装 安装MariaDB&#xff08;数据库&#xff09;安装RabbitMQ&#xff08;消息队列&#xff09;安装Memcached&#xff08;缓存&#xff09; 安装MariaDB&#xff08;数据库&#xff09; 安装 # yum install mariadb mariadb-server python2-PyMySQL -y数据库配置 …

Springboot2+thymeleaf+Jpa实现CRUD操作

1 引入pom依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency><groupId>org.springframework.boot</groupId><artifactId…

122 - Trees on the level (UVA)

题目链接如下&#xff1a; Online Judge 我的代码如下&#xff08;tot 是目前没有赋值的node数目&#xff09;&#xff1a; #include <iostream> #include <string> #include <cstdlib> #include <vector> // #define debugstruct node{int key -1…

Python 打开多个文件

Python 打开文件使用open 打开多个文件可是使用写多个open &#xff0c;如果是2个或者3个数量很少的情况可以 写2个或者3个open 打开 with open(r"文件名1", "r", encoding"utf-8") as f1, open(r"文件名2", "r", encodi…