引言

       作用域与作用域链是JS应用中无时无刻不在影响程序运行的关键属性,但是由于它的不可见性,或者说它存在的过于普遍,简直就像空气一样。所以对它的谈及,都很简单,而理解起来也不复杂。



       但是由于它的重要性,对它做一个篇幅的说明,也是一件理所应当的事情。而且它其实也并没有理解上那么简单。



       本文对作用域及作用域链的说明,可能并不全面。笔者尽量以自身开发过程中的理解,做到表述全面。

1、执行环境

       按照《JavaScript高级程序设计》第3版中的定义——执行环境定义了变量或函数有权访问其他数据,决定了它们各自的行为。每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。

       对于环境的简单理解,可以理解为{}一个大括号就是一个执行环境。如{{}},就是包含关系的两个执行环境。而由于所有的函数和变量,最终都是通过最外围有对应的调用,才能最终实现运行,所以将最外围的执行环境定义为全局执行环境,在宿主为web浏览器时,全局执行环境就为window对象

       每个函数都有自己的执行环境。当执行程序进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将环境弹出,把控制权返回给之前的执行环境。之后,该环境因为执行完毕,随即被销毁。但有一个例外,全局环境将永远不会被销毁,直到程序被关闭,如关闭浏览器。

由上图我们清晰的看到JavaScript的两个阶段的执行流程。

2、作用域与作用域链

       作用域链和作用域是一个包含关系。当代码在一个环境中执行时,会创建变量对象的一个作用域链。而作用域链中,包含的就是一个个有序排列的作用域。作用域链的作用,就是保证执行环境中的变量和函数的有序访问

       每次的变量或函数声明,都会形成一个标识符(即变量名或函数名)。 作用域链中的向上查找,实际上就是标识符的查找。找到匹配的标识符,就调用它的值。

       作用域链中包含的每个作用域对象,其中都包含着它自身环境中可访问的变量或函数(script作用域中,为全部的变量和函数;函数作用域中,子函数调用父函数环境中的变量或函数会形成闭包,那么可访问变量就为闭包调用的变量)。换一种角度理解的话,这些环境中可访问的变量或函数就是标识符,是当前作用域中可通过作用域链向上查找的标识符。

       一大段的文字不光读起来枯燥,理解起来也困难。所以,stop talking,show code。


以函数为例

function test() {
let a = 1; //注释此行,保留其他a,c()打印2,并且作用域链失去闭包对象
let b = 0; return () => { //形成闭包环境
console.log(a);
}
} let a = 2; //注释此行及test中的a,c()打印3
window.a = 3; //注释全部a,报错 const c = test();
c(); console.dir(test);
console.dir(c);

以上代码,通过**3次注释**以及**chrome浏览器打印**,可以清晰的看到作用域与作用域链的形成及作用域链**按顺序查找**的特性

1)首先是闭包调用——所有a都不注释



由上图可以看出,此时作用域链中存在3个作用域对象。 按索引顺序为0—Closure闭包环境、1—Script标签环境、2—Global全局环境;

索引顺序就是标识符的查找顺序,所以当执行函数c()时,先在Closure闭包环境中查找,然后是Script标签作用域,最后是Global全局作用域。因为形成了闭包,那么标识符的查找肯定能在闭包环境中找到对应结果,所以打印1;

值得注意的一点是,Closure与Script作用域对象中,存储的标识符的不同。closure只存储调用值a = 1,并没有存储b = 0。而Script中将环境中的所有标识符都存在了对象中;

2)然后是取消闭包调用,改为Script标签作用域查找——注释test中的a = 1



由上图可以看出,此时作用域链中的Closure闭包作用域消失,只剩下2个对象0—Script标签环境、2—Global全局环境;

那么同样按索引顺序查找,找到了Script标签环境中的a = 2,没有继续寻找Global全局环境中的window.a = 3,最终打印2;

3)最后是Global全局作用域查找——注释test中的a = 1及script中的a = 2



与2)中情况对比,作用域链中的对象没有改变。但是Script对象中的标识符,随着a = 2被注释而消失

按索引顺序查找,找到了Script标签环境中没有a,继续寻找Global全局环境,最终找到window.a = 3,打印3;



函数test的打印,从始至终都是Script标签作用域及Global全局作用域。理解了c函数的作用域链,自然就明白了函数test的,所以不再展开赘述。

如有错误或阐述不充分之处,欢迎指正~

javascript精雕细琢(三):作用域与作用域链的更多相关文章

  1. 一步步学习javascript基础篇(2):作用域和作用域链

    作用域和作用域链 js的语法用法非常的灵活,且稍不注意就踩坑.这集来分析下作用域和作用域链.我们且从几道题目入手,您可以试着在心里猜想着答案. 问题一. if (true) { var str = & ...

  2. 关于Javascript作用域及作用域链的总结

    本文是根据以下文章以及<Javascript高级程序设计(第三版)>第四章相关内容总结的. 1.Javascript作用域原理,地址:http://www.laruence.com/200 ...

  3. JavaScript的作用域与作用域链

    作用域 作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期.可以说,变量和函数在什么时候可以用,什么时候被摧毁,这都与作用域有关. JavaScript中,变量的作用域有全局 ...

  4. javascript作用域和作用域链摘录

    作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理.今天这篇文章对JavaScript作用域和作用域链作简单的介绍,希望 ...

  5. JavaScript高级之词法作用域和作用域链

    主要内容: 分析JavaScript的词法作用域的含义 解析变量的作用域链 变量名提升时什么 一.关于块级作用域         说到JavaScript的变量作用域,与咱们平时使用的类C语言不同. ...

  6. JavaScript 开发进阶:理解 JavaScript 作用域和作用域链

    作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理.今天这篇文章对JavaScript作用域和作用域链作简单的介绍,希望 ...

  7. JavaScript 开发进阶:理解 JavaScript 作用域和作用域链(转载 学习中。。。)

    作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理.今天这篇文章对JavaScript作用域和作用域链作简单的介绍,希望 ...

  8. Javascript的作用域、作用域链以及闭包

    一.javascript中的作用域 ①全局变量-函数体外部进行声明 ②局部变量-函数体内部进行声明 1)函数级作用域 javascript语言中局部变量不同于C#.Java等高级语言,在这些高级语言内 ...

  9. JavaScript 作用域和作用域链

    作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理.今天这篇文章对JavaScript作用域和作用域链作简单的介绍,希望 ...

随机推荐

  1. 项目Beta冲刺(团队)第六天

    1.昨天的困难 可以获得教务处通知栏的15条文章数据了,但是在显示的时候出了问题. 私信聊天的交互还没研究清楚 2.今天解决的进度 成员 进度 陈家权 研究私信模块 赖晓连 研究问答模块 雷晶 研究服 ...

  2. caffe可视化模型

    进入$CAFFE_ROOT/python: $ python draw_net.py ../models/bvlc_reference_caffenet/train_val.prototxt caff ...

  3. 处理Git不能上传大于100M文件问题

    记录一下自己工作遇到的问题,免得下次再遇到了还到处网上查资料解决. 自己的项目的版本控制用的是Git,代码仓库在github托管.项目里用到了百度导航SDK,由于百度导航SDK有了新版本,于是就更新到 ...

  4. n位格雷曼实现

    参考: 格雷码的实现 问题:产生n位元的所有格雷码.   格雷码(Gray Code)是一个数列集合,每个数使用二进位来表示,假设使用n位元来表示每个数字,任两个数之间只有一个位元值不同. 例如以下为 ...

  5. 关于对i++,++i的理解

    i++,代表 先赋值,在加:++i,代表先自加再赋值:后台console例子中可以看到第一个例子:var a= i++;  i是等于1的:先赋值,所以打印出a =1的:而i++后为2:所以打印出a = ...

  6. mysql 时间格式化参数表笔记

    DATE_FORMAT() 函数用于以不同的格式显示日期/时间数据. 语法: DATE_FORMAT(date,format) 实例: DATE_FORMAT(NOW(),'%b %d %Y %h:% ...

  7. 是否升级IOS11?IOS11不支持32位程序 查看手机哪些APP不支持

    查看苹果32位APP具体步骤:设置-通用-关于本机-应用程序.如果手机中下载了32位应用的话,苹果会给出应用兼容性提醒:如果手机里没有安装32位应用,右侧没有小三角,点击“应用程序”也会没有反应. I ...

  8. 打开eclipse编译后的.class文件

    众所周知,用文本编辑器打开.class文件会乱码.我们可以使用命令行打开.class文件项目结构: 代码: public class Synchronized { public static void ...

  9. 【刷题】BZOJ 1565 [NOI2009]植物大战僵尸

    Description Plants vs. Zombies(PVZ)是最近十分风靡的一款小游戏.Plants(植物)和Zombies(僵尸)是游戏的主角,其中Plants防守,而Zombies进攻. ...

  10. AtCoder Grand Contest 002

    AtCoder Grand Contest 002 A - Range Product 翻译 告诉你\(a,b\),求\(\prod_{i=a}^b i\)是正数还是负数还是零. 题解 什么鬼玩意. ...