博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
进击JavaScript之(二)词法作用域与作用域链
阅读量:7156 次
发布时间:2019-06-29

本文共 2046 字,大约阅读时间需要 6 分钟。

一、作用域

域表示的就是范围,即作用域,就是一个名字在什么地方可以使用,什么时候不能使用。想了解更多关于作用域的问题推荐阅读《你不知道的JavaScript上卷》第一章(或第一部分),从编译原理的角度说明什么是作用域。概括的说作用域就是一套设计良好的规则来存储变量,并且之后可以方便地找到这些变量。

1.1 块级作用域

在C、Java、C#等编程语言中,下面的语法报错(伪代码)

{    var num = 123;    {        console.log( num );    // num => 123     }}console.log( num );   //报错

1.2 JS的词法作用域

所谓的词法(代码)作用域,就是代码在编写过程中体现出来的作用范围,代码一旦写好,不用执行,作用范围就已经确定好了,这个就是所谓的词法作用域。

在JS中词法作用域的规则:

  1. 函数允许访问函数外部的数据
  2. 整个代码结构中只有函数可以限定作用域
  3. 作用规则首先使用提升规则分析
  4. 如果当前作用域中有了名字了,就不考虑外面的名字

例子1:

var num = 123;function foo(){    console.log( num );}foo();    //123

例子2:

if( false ){    var num = 123;}console.log( num ); //undefined

例子3:

var  num = 123;function foo () {    var num = 456;    function func(){        console.log( num );     }     func();}foo();    //456

二、作用域链

只有函数才能制造作用域结构,那么只要是代码,至少有一个作用域,即全局作用域。

凡是代码中有函数,那么这个函数就构成另一个作用域。如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域,那么将这样的所有作用域列出来,可以有一个结构:函数内指向函数外的链式结构

例如:

function f1() {    function f2() {    }}var num = 456;function f3() {    function f4() {    }}

作用域链结构与DOM树结构很相似.

2.1 绘制作用域链

步骤:

  1. 看整个全局是一条链,即顶级链,记为0级链
  2. 看全局作用域中有什么成员声明,就以方格的形式绘制到0级链上
  3. 再找函数,只有函数可以限制作用域,因此从函数中引出新链,标记为1级链
  4. 然后在每一个1级链中再次往复刚才的行为

图片描述

2.2 变量的访问(搜索)规则

  1. 首先看变量在第几条链上,在该链上看是否有变量的定义与赋值,如果有直接使用
  2. 如果没有到上一级链上找( n - 1 级链),如果有直接使用,停止继续查找。
  3. 如果还没有在此往上找… 直到全局链(0级),还没有就是is not defined
  4. 注意,切记:同级的链不可混合查找

绘制如下程序的作用域链

function f1() {    var num = 123;    function f2() {        console.log( num );      }    f2();}var  num = 456;f1();    //123

图片描述

函数f1 和变量 num=456, 在0级链,而f1下又可以展开1级链,1级链上有num=123和函数f2。程序f1()调用进入左边1级链,而f1中又调用了f2函数,f2函数中console.log(num)可以看作在2级链,此时,程序会向这一条链向上查找,首先2级链没有num,向上到达1级链,刚好1级链上有num=123,所以就直接使用123,程序最后的结果就是打印123.

2.3 如何分析代码

  1. 在分析代码的时候切记从代码的运行角度上来分析,如果代码给变量赋值了,一定要标记到图中
  2. 如果代码比较复杂,可以在图中表示代码的内容,有时候还要将原型图与作用域图结合起来分析

分析如下代码:

var num = 123;function f1() {    console.log( num );}function f2(){    var num = 456;    f1();}f2();    //123

作用域链图:

图片描述

首先把num=123,函数f1,函数f2画在0级链上。f1中只有一句console,画出一条1级链,f2也下画出1级链,链上有num=456和函数调用语句f1();

调用f2(),进入f2函数的作用域链,而在f2中又调用了f1函数,程序进入f1的作用域链,所以console.log(num)会在此链上查找是否存在num,没有,继续向上一级链查找,刚好在0级链上找到了num=123,所以f1函数中的console.log(num)就是123.


推荐阅读

转载地址:http://lkrgl.baihongyu.com/

你可能感兴趣的文章
C++Singleton的DCLP(双重锁)实现以及性能测评
查看>>
Cocos2d-x 内存管理原则
查看>>
android CheckBox与监听
查看>>
snmpwalk的报文检测
查看>>
mysql数据库中char和varchar类型比较
查看>>
Webform Repeater的灵活运用
查看>>
深浅拷贝
查看>>
Django的序列化_通过ajax方式
查看>>
浅析tnsping
查看>>
215.Spring Boot+Spring Security:初体验
查看>>
js数组操作记录
查看>>
SQL 语句 写法
查看>>
【个人笔记】CLR Via C# 第一章 CLR执行模型
查看>>
Mysql中将查询出来的多列的值用逗号拼接
查看>>
字节序和网络字节序【转】
查看>>
有关UnrealEngine材质编辑器中的Custom节点的一些小贴士
查看>>
Ue4全景图制作设想
查看>>
spring-mvc.xml 和 application-context.xml的配置与深入理解
查看>>
定时器
查看>>
html5-output的用法
查看>>