本篇文章在于详细解读JavaScript的作用域,从底层原理来解释一些常见的问题,例如变量提升、隐式创建变量等问题,在和大家一起交流进步的同时,也算对自己知识掌握的记录,方便以后复习

  首先,直接捡干的来,JS作用域大致分为三部分:词法作用域、函数作用域/块作用域、闭包。

  在传统的编译语言中,程序的源代码编译由三个步骤组成:词法分析、语法分析、代码生成。而JS属于动态语言,它的编译过程不发生在构建之前,而是在代码执行前(一般只有几微妙,甚至更短),简单说,任何JS代码执行前都要编译,编译完通常马上就要执行。

  例如: var a = 2; 将其分解为以下步骤:

  1.遇到   var a  编译器会询问作用域是否已经存在同名变量于同一个作用域的集合中,若存在,则忽略该声明。若不存在,编译器在当前作用域声明一个新变量a。

  2.接下来编译器会为引擎生成运行时的代码,这些代码用于处理  a = 2的赋值操作。引擎运行时会询问作用域,当前作用域是否存在变量a,若存在,引擎直接使用该变量。否则引擎继续向上查找,直到顶层全局作用域还未找到,则会抛出ReferenceError,如果找到,就会将2赋值给它。

  在上面的例子中,引擎有两种查询方式:LHS、RHS。

  其中L、R代表“左”、“右”,是相对于赋值操作的左右,当变量出现在赋值操作的左侧时进行LHS查询,出现在右侧时进行RHS查询,也可以这么理解,RHS查询是找到某个变量的值,而LHS是找到变量的容器本身!!!即作用域中开辟的变量存放空间。举个例子:如下代码   console.log(a); 引擎对a的查询就是RHS,这里没有赋值操作,需要查找a的值,并把它传给console.log(..);函数。在逐级向上查找中,直到全局也没找到,则抛出ReferenceError。但LHS若没找到是不会抛出错误的。具体原因继续看。

  举个例子:  a= 2; 这里对a的引用则是LHS引用,我们并不关心但前值是多少,只是想要为 =2 这个赋值操作找到合法目标,可能有童鞋疑问,=2不就是赋给a的嘛?对啊,但是a到底存不存在呢?在当前作用域中,我们是不知道是否创建了a的存储空间的,如果作用域中存在 var a ,那么该a的存储空间存在,LHS能成功,但是没有a的存储空间呢?也就是a并未创建呢?此时,LHS也不会抛出错误,而是隐式的在当前作用域(全局作用域、即最高层作用域,一层一层找上去的)为我们创建变量a的存储空间,然后把 =2 赋值给a。这也就是为啥 var a =2; 创建的是局部变量,而没有 var 申明的变量是全局变量的原因。

  作用域的嵌套

  

  作用域就是一套如何存储和查找变量的规则。在嵌套作用域中,如上图,在foo()中无法找到变量 b,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量,或者到达最顶层(全局作用域)。上图想要执行console.log()函数,就要对b进行RHS查找,得到其值。才foo中无法完成b的RHS,但在外层中却可以完成。即:引擎从当前的执行作用域中开始查找变量,找不到,则逐级向上查找,直到最顶层作用域。

  区分LHS和RHS

  区分两种查询方式很重要,因为上文简单提到RHS找不到会抛出ReferenceError,而LHS则不会,它会隐式创建所需的变量。如下               

  对b的RHS查找失败,因为没有声明(创建)变量b,未声明的变量,在任何作用域中都无法找到!那么,在上图中,只要把 b= 2放在console.log()之前,函数就成功执行了,因为第一步执行 b= 2;赋值操作进行LHS,找不到,则在全局中隐式创建变量b,此时使用 window.b 是可以得到2的。

词法作用域

  词法作用域就是定义此法阶段的作用域,即你写代码时将变量和作用域写在哪里而决定其作用范围。作用域在查找到第一个标识符时即停止查找,多层嵌套的作用域中同名的标识符,内部遮蔽外部(遮蔽效应),全局遮蔽可用window.得到其值,而局部遮蔽的则无论如何都无法被访问到。无论函数在哪里被调用,以何种方式调用,其词法作用域都只由被声明时所处的位置决定,即你写下哪他就在哪发挥作用。

  

上图中,全局作用域中只有一个标识符,即foo,函数foo作用域中有三个标识符,即b,bar ,a 。函数bar里面只有一个标识符 c 。其每个标识符处于不同的作用域中,而代码运行时会以他们不同的位置而访问权限不同。这些位置在书写时已经被我们写死了,他们的作用被我们写好了,这就是词法作用域!代码的位置真的被我们“写死了嘛”?接着看

  词法欺骗

  词法作用域由写代码时声明的位置决定,也可以由两种机制来动态改变词法作用域。

  1.  eval()函数。可接受一个字符串为参数,将其中内容视为好像在书写时就在这个地方的代码。可以理解为我在梦中就是高富帅,真实的就连后面的剧情(梦中剧情,哈哈)都是以高富帅为基础开展的,不知道这个比喻贴切不?即就是eval()可以让里面的参数代码段达到书写时就在这个地方的效果。如下:

  

  输出结果为a:5,b:8,而不是a= 2,根据词法作用域中。foo中找不到a,则到上一层作用域中寻找,上一层中找到了 a = 2 ;。可是eval()函数却欺骗了词法作用域,直接将a放在了foo内部,而导致引擎不需要到外层作用域去查找,直接使用 a = 5 ,从而达到此法欺骗。

2.  with语句。with 语句通常用作重复引用一个对象的多个属性的快捷方式。代码如下:

  

  with语句也可以达到欺骗词法的作用,但是副作用也很明显,造成了变量泄露。原因是调用obj2的时候,其没有变量a,进行LHS查询,最后隐式创建全局变量属性a ,导致变量泄露。

  以上两种词法欺骗方式,第一严重影响性能,第二在严格模式下有诸多限制,所以不建议使用。函数作用域和闭包近期在整理,过几天推出

  作者:方红亮  

  博客:https://www.cnblogs.com/fanghl/p/9369414.html

  今天只介绍了作用域和词法作用域,希望对小伙伴理解有所帮助,分享转载的朋友请注明出处,码字不易,谢谢理解!

JS中的作用域(一)-详谈的更多相关文章

  1. 聊一下JS中的作用域scope和闭包closure

    聊一下JS中的作用域scope和闭包closure scope和closure是javascript中两个非常关键的概念,前者JS用多了还比较好理解,closure就不一样了.我就被这个概念困扰了很久 ...

  2. JS中的作用域以及全局变量的问题

    一. JS中的作用域 1.全局变量:函数外声明的变量,称为全部变量 局部变量:函数内部使用var声明的变量,称为局部变量在JS中,只有函数作用域,没有块级作用域!!!也就是说,if/for等有{}的结 ...

  3. 【详解】JS中的作用域、闭包和回收机制

    在讲解主要内容之前,我们先来看看JS的解析顺序,我们惯性地觉得JS是从上往下执行的,所以我们要用一个变量来首先声明它,来看下面这段代码: alert(a); var a = 1; 大家觉得这段代码有什 ...

  4. JS中的作用域及闭包

    1.JS中的作用域 在 es6 出现之前JS中只有全局作用域和函数作用域,没有块级作用域,即 JS 在函数体内有自己的作用域,但是如果不是在函数体的话就全部都是全局作用域.比如在 if.for 等有 ...

  5. 【授课录屏】JavaScript高级(IIFE、js中的作用域、闭包、回调函数和递归等)、MySQL入门(单表查询和多表联查)、React(hooks、json-server等) 【可以收藏】

    一.JavaScript授课视频(适合有JS基础的) 1.IIFE 2.js中的作用域 3.闭包 4.表达式形式函数 5.回调函数和递归 资源地址:链接:https://pan.baidu.com/s ...

  6. angular.js 中的作用域 数据模型 控制器

    1.angular.js 作为后起之秀的前端mvc框架,他于传统的前端框架都不同,我们再也不需要在html中嵌入脚本来操作对象了.它抽象出了数据模型,控制器及视图. 成功解耦了应用逻辑,数据模型,视图 ...

  7. JS中的作用域和作用域链

    本文原链接:https://cloud.tencent.com/developer/article/1403589 前言 作用域(Scope) 1. 什么是作用域 2. 全局作用域和函数作用域 3. ...

  8. 浅析 JS 中的作用域链

    作用域链的形成 在 JS 中每个函数都有自己的执行环境,而每个执行环境都有一个与之对应的变量对象.例如: var a = 2 function fn () { var a = 1 console.lo ...

  9. JS中的作用域链

    在js中数据的声明方式有两种: 1.用var声明,例如:var num = 10: 2.直接声明,例如:num = 10: 两种声明方式在某些情况下是有区别的: var data = 10; func ...

随机推荐

  1. 解析url成对象形式

    请编写一个JavaScript函数parseQueryString,他的用途是把URL参数解析为一个对象 var url = "https://www.baidu.com/s?ie=utf- ...

  2. 一款非常好用的 Windows 服务开发框架,开源项目Topshelf

    Topshelf是一个开发windows服务的比较好的框架之一,以下演示如何开发Topshelf服务. 1.首先打开你的vs.新建一个TopshelfStudy控制台程序,如下图所示: 这是我用vs2 ...

  3. 阿里云windows 2008 服务器处理挖矿程序 Miner

    阿里云盾最近报发现wanacry蠕虫病毒和挖矿进程异常 仔细检查进程后,发现两个奇怪的进程 Eternalblue-2.2.0.exe,winlogins.exe 特别是伪装成 winlogins.e ...

  4. COMP 321

    COMP 321April 24, 2019Questions on this exam may refer to the textbook as well as to the manual page ...

  5. xpath解析数据

    xpath解析数据 """ xpath 也是一种用于解析xml文档数据的方式 xml path w3c xpath搜索用法 在 XPath 中,有七种类型的节点:元素.属 ...

  6. mysql 数据备份和还原

    使用mysqldump命令备份 使用root用户备份test数据库下的person表 mysqldump -u root -p test person > D:\backup.sql 备份多个数 ...

  7. logstash 抓取IIS日志文件写入Elasticsearch

    如果需要对IIS日志进行分析可以使用logstash从文件中抓取出来进行分析: 输入部分: input { file { type => "iis_log_monitor" ...

  8. vue 开发环境搭建

    https://www.cnblogs.com/goldlong/p/8027997.html 1,按照nodeJS. 2,nodejs 包含npm. 3, 执行: npm install -g @v ...

  9. Chrom Firefox 非安全端口访问

    使用Chrom Firefox 访问非安全端口 问题描述 Firefox 此网址已被限制 此网址使用了一个通常用于网络浏览以外目的的端口.出于安全原因,Firefox 取消了该请求. Chrome 无 ...

  10. 线程(Thread,ThreadPool)、Task、Parallel

    线程(Thread.ThreadPool) 线程的定义我想大家都有所了解,这里我就不再复述了.我这里主要介绍.NET Framework中的线程(Thread.ThreadPool). .NET Fr ...