作用域与作用域链

news2024/9/29 7:18:11

javascript拥有一套设计良好的规则来存储变量,并且之后可以方便的找到这些变量,这套规则叫做作用域。

内部原理

内部原理分成编译、执行、查询、嵌套和异常五部分。今天来简单说一下查询。

var a=2;

这行代码中,在引擎执行的第一步操作中,对变量a进行了查询,这种查询叫做LHS查询。实际上,引擎查询共分为两种:LHS查询和RHS查询。
从字面意思去理解,当变量出现在赋值操作的左侧时进行LHS查询,出现在右侧时进行RHS查询。
更准确的是,RHS查询与简单的查找某个变量的值没什么区别,而LHS查询则是试图找到变量的容器本身,从而可以对其赋值。

function foo(){
	console.log(a);//2
}
foo(2);

这段代码中,总共包括4个查询,分别是:
1.foo(…)对foo进行了RHS引用
2.函数传参a=2对a进行了LHS引用
3.console.log(…)对console对象进行了RHS引用,并检查其是否有一个log的方法。
4.console.log(a)对a进行了RHS引用,并把得到的值传给了console.log(…)

问题来了,为什么要区别LHS和RHS呢?
因为在变量还没有声明的时候,这两种查询的行为不一样:
RHS
【1】如果RHS查询失败,引擎会抛出ReferenceError(引用错误)异常。

//对b进行RHS查询时,无法找到该变量。也就是说,这是一个“未声明”的变量
function foo(a){
    a = b;  
}
foo();//ReferenceError: b is not defined

【2】如果RHS查询找到一个变量,但尝试对变量的值进行不合理操作,比如对一个非函数类型值进行函数调用,或者引用null或undefined中的属性,引擎会抛出另一种类型异常:TypeError(类型错误)异常。

function foo(){
    var b = 0;
    b();
}
foo();//TypeError: b is not a function

LHS
【1】当引擎执行LHS查询时,如果无法找到变量,全局作用域会创建一个具有该名称的变量,并将其返还给引擎。

function foo(){
    a = 1;  
}
foo();
console.log(a);//1

【2】如果在严格模式中LHS查询失败时,并不会创建并返回一个全局变量,引擎会抛出同RHS查询失败时类似的ReferenceError异常

function foo(){
    'use strict';
    a = 1;  
}
foo();
console.log(a);//ReferenceError: a is not defined

词法作用域和动态作用域

词法作用域
就是定义在词法阶段的作用域,是由写代码时将变量和块作用域写在哪里决定的。无论函数在哪里被调用,也无论他如何被调用,他的词法作用域都只由函数被声明时所处的位置决定。

function foo(a) {
    var b = a * 2;
    function bar(c) {
        console.log( a, b, c );
    }
    bar(b * 3);
}
foo( 2 ); // 2 4 12

在这个例子中,有三个逐级嵌套的作用域。为了帮助理解,可以将它们想象成几个逐级包含的气泡。
在这里插入图片描述
作用域气泡由其对应的作用域块代码写在哪里决定,它们是逐级包含的

气泡1包含着整个全局作用域,其中只有一个标识符:foo

气泡2包含着foo所创建的作用域,其中有三个标识符:a、bar和b

气泡3包含着bar所创建的作用域,其中只有一个标识符:c

在查找的过程中,引擎首先会从最内部的作用域来查找,如果没有找到,引擎会到上一级所嵌套的作用域中去继续查找。

【注意】词法作用域查找只会查找一级标识符,如果代码引用了foo.bar.baz,词法作用域查找只会试图查找foo标识符,找到这个变量后,对象属性访问规则分别接管对bar和baz属性的访问。

foo = {
    bar:{
        baz: 1
    }
};
console.log(foo.bar.baz);//1

作用域查找从运行时所处的最内部作用域开始,逐级向外或者说向上进行,直到遇见第一个匹配的标识符为止。在多层的嵌套作用域中可以定义同名的标识符,这叫作“遮蔽效应”,内部的标识符“遮蔽”了外部的标识符。

var a = 0;
function test(){
    var a = 1;
    console.log(a);//1
}
test();

全局变量会自动为全局对象的属性,可以通过对全局对象属性的引用来对其进行访问。

var a = 0;
function test(){
    var a = 1;
    console.log(window.a);//0
}
test();

【注意】:但是如果是非全局的变量被遮蔽了,无论如何也无法被访问到了。
动态作用域
javascript使用的是词法作用域,它最重要的特征就是它的定义过程发生在代码的书写阶段。
但是呢,动态作用域不太一样,他关心它们什么时候被调用,在何处被调用。

声明提升

包含变量和函数在内的所有声明都会在任何代码被执行前被处理

变量声明提升

var a=2;

这个代码片段实际上包括两个操作:var a和a=2
第一个定义声明是在编译阶段进行的。第二个赋值操作会被留在原地等待引擎在执行阶段执行。

//对变量a的声明提升到最上面后,再执行代码时,控制台输出2
var a;
a = 2 ;
console.log(a);

函数声明提升

foo();
function foo(){
    console.log(1);//1
}

上面代码片段之所以能够在控制台输出1,就是因为foo()函数声明进行了提升,如下:

function foo(){
    console.log(1);
}
foo();

【注意】函数声明会提升,但是函数表达式不会提升

foo();
var foo = function(){
    console.log(1);//TypeError: foo is not a function
}

提升后是:

//变量提升后,代码如下所示:
var foo;
foo();
foo = function(){
    console.log(1);
}

函数覆盖
函数声明和变量声明都会被提升。但是,函数声明会覆盖变量声明。

var a;
function a(){}
console.log(a);//'function a(){}'

但是!!!如果变量存在赋值操作,那么最终的值为变量的值!!!

var a=1;
function a(){}
console.log(a);//1
var a;
function a(){};
console.log(a);//'function a(){}'
a = 1;
console.log(a);//1

块作用域

随着ES6的推广,块作用域用的越来越广泛。
ES6引入了let和const关键字,和var关键字不同,在大括号中使用let和const声明的变量存在于块级作用域中。在大括号之外不能访问这些变量。

{
  // 块级作用域中的变量
  let greeting = 'Hello World!';
  var lang = 'English';
  console.log(greeting); // Prints 'Hello World!'
}
// 变量 'English'
console.log(lang);
// 报错:Uncaught ReferenceError: greeting is not defined
console.log(greeting);

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

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

相关文章

【C++ Primer】阅读笔记(3):decltype

目录 简介decltype基础decltype需要注意的地方1.decltype处理顶层const、引用与auto不同2.如果decltype使用的表达式不是一个变量,则decltype返回表达式结果对应的类型。3.如果表达式的内容是解引用操作,则decltype会得到引用类型。4.对于decltype所用的表达式来说,如果变量…

Spring AOP统一功能处理

⭐️前言⭐️ 这篇文章主要介绍AOP(Aspect Oriented Programming)——面向切面编程的思想,它是对某一类事情的集中处理,也是对OOP(Object Oriented Programming)面向对象编程的补充和完善。 🍉…

【编程语言选择】我们学C++将来能做什么?

首先贴上C嘎嘎祖师爷的镇楼帅照😆 凝视目录 什么是C C的使用广泛度 C的具体工作领域有什么 什么是C 简单说 C是基于C语言而产生的,它既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可…

【区块链 | 前端】前端开发人员入门区块链的最佳实践

前端开发人员入门区块链的最佳实践 一. 建立信仰 从技术入门一个行业通常是漫无目的,个人认为正确的入行区块链的方式是去了解他的背景,是去建立自己信仰的,尤其身处一个刚起步就被扼杀的行业,我们每个人都是领头人,我…

06栈和队列

开始系统学习算法啦!为后面力扣和蓝桥杯的刷题做准备!这个专栏将记录自己学习算法是的笔记,包括概念,算法运行过程,以及代码实现,希望能给大家带来帮助,感兴趣的小伙伴欢迎评论区留言或者私信博…

[NOIP 2003] 栈(三种方法:DP、数论、搜索)

[NOIP2003 普及组] 栈 题目背景 栈是计算机中经典的数据结构,简单的说,栈就是限制在一端进行插入删除操作的线性表。 栈有两种最重要的操作,即 pop(从栈顶弹出一个元素)和 push(将一个元素进栈&#xff…

5 个必须尝试的无代码应用

无代码软件让任何人无需了解编程语言即可构建产品、网站和应用程序。Editor XWix 发布了Editor X,这是一款无需编写任何 CSS (层叠样式表)或 HTML(超文本标记语言)代码的新型拖放式网站构建器,为设计师和机…

如何把可观测需求落地为业务大盘?

2022 年 9 月 28 日,阿里云用户组(AUG)第 11 期活动在深圳举办。活动现场,阿里云技术专家李加贝向参会企业代表分享了如何把可观测需求落地为业务大盘的议题。本文根据现场分享内容整理而成。 为什么需要 Grafana? 演…

智能合约Smart Contract技术详解

文章目录合约编写基本介绍构造方法ipfsmint提现白名单合约前端部署验证合约代码前端和合约交互准备工作获取已经mint了的数量mint合约编写 建议读者先了解下solidity,这里推荐CryptoZombies,还是比较详细的。 ok当你大概知道自己在做什么之后&#xff0…

【概率论】期末复习笔记:假设检验

假设检验目录一、假设检验的基本概念1. 假设检验的基本原理2. 两类错误3. 假设检验的一般步骤4. ppp值二、正态总体参数的假设检验σ2已知,检验μ与μ0的关系\color{dodgerblue}\sigma^2\text{已知,检验}\mu\text{与}\mu_0\text{的关系}σ2已知&#xff…

上海华清远见

解析设备树节点信息实例1获取属性数值实例2获取u32类型的值将获取到的u32类型的值存放在array数组中

什么是许可式邮件营销?

邮件是很多企业日常中用到的信息传播工具,并且它还具备了成本低、长期性等优点,所以很多企业选择使用邮件作为载体进行营销推广。而在进行邮件营销的时候,不同的方式和技巧也会影响到最终的营销效果。为了达到较好的营销效果,很多…

Seata应用

下载seata-server 下载地址:Tags seata/seata GitHub 配置Seata-server 第一步:配置seata-server数据源 E:\seata-server-1.4.2\seata\seata-server-1.4.2\conf\file.conf 第二步:创建seata数据库 create database seata 第三步&#xf…

单元测试-SpringBoot Test和Mock

单元测试-SpringBoot Test和Mock “单元测试” “junit,mock,桩” 1. 什么是单元测试 定义:是指对软件中的最小可测试单元进行检查和验证。 Java里单元指一个方法。单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的…

玻纤效应对skew的影响(三)

玻纤效应对skew的影响(一)玻纤效应对skew的影响(二)对内skew对32Gbps NRZ和64Gbps PAM-4的影响这一篇中,玻纤效应造成的对内skew将会加入到32Gbps NRZ和64Gbps PAM-4 SerDes全链路分析中。PCIe 5.0代表32Gbps NRZ&…

C++GUI之wxWidgets(11)-编写应用涉及的类和方法(6)-事件处理(5)

目录自定义事件wxPostEvent()wxQueueEvent()PopEventHandler()Bind()GetEventUserData()Connect()Unbind()定义自己的事件类事件处理程序与虚拟方法自定义事件 wxPostEvent() void wxPostEvent ( wxEvtHandler * dest,const wxEvent & event ) 在GUI应用程序中&am…

云开发项目中如何管理用户和管理授权?

管理用户 在项目中添加用户后,才能为用户授予对应的资产管理权限。支持修改已创建用户的密码和删除用户。 本文中的 用户 是指在云项目下创建的 B 端子账号,可以和资产授权配合使用,管理 B 端设备和资产。这些 B 端用户账号可以在 智慧行业…

IOS开发基础 · SwiftUI · CS193p Lecture1-2

IOS开发Lecture 1TextRoundedRectangleZstackLecture 2HStackstruct整合组件ContentViewstruct 中创建变量var&letSwiftUI刷新重建点击效果ArrayForeachButtonSpacervar整合小组件SF-symbol上下界限制简化ButtonLecture 1 Text import SwiftUIstruct ContentView: View {…

Node.js 中 cookie的验证登录

认识 cookie 在讲cookie的登录验证之前,先来了解一下cookie是什么?cookie本质是存储在浏览器中的一小段文本信息(不超过4kb),是由服务器生成发送到浏览器(客户端),浏览器将其保存在…

虚拟化技术学习笔记2

1、虚拟机与容器对比: 2、Hypervisor管理工具对比: 3、QEMU: 软件模拟虚拟化、可以模拟多种硬件,包括X86架构处理器、AMD64架构处理器、ARM、SPARC与PowerPC、AIX架构等,效率低、一般用于研究测试场景。QEMU可以模拟一…