深入理解javascript作用域系列第五篇——一张图理解执行环境和作用域
前面的话
对于执行环境(execution context)和作用域(scope)并不容易区分,甚至很多人认为它们就是一回事,只是高程和犀牛书关于作用域的两种不同翻译而已。但实际上,它们并不相同,却相互纠缠在一起。本文先用一张图开宗明义,然后进行术语的简单解释,最后根据图示内容进行详细说明
图示
概念
【作用域】
作用域是一套规则,用于确定在何处以及如何查找标识符。关于LHS查询和RHS查询详见作用域系列第一篇内部原理。
作用域分为词法作用域和动态作用域。javascript使用词法作用域,简单地说,词法作用域就是定义在词法阶段的作用域,是由写代码时将变量和函数写在哪里来决定的。于是词法作用域也可以描述为程序源代码中定义变量和函数的区域
作用域分为全局作用域和函数作用域,函数作用域可以互相嵌套
在下面的例子中,存在着全局作用域,fn作用域和bar作用域,它们相互嵌套
【作用域链和自由变量】
各个作用域的嵌套关系组成了一条作用域链。例子中bar函数保存的作用域链是bar -> fn -> 全局,fn函数保存的作用域链是fn -> 全局
使用作用域链主要是进行标识符的查询,标识符解析就是沿着作用域链一级一级地搜索标识符的过程,而作用域链就是要保证对变量和函数的有序访问
【1】如果自身作用域中声明了该变量,则无需使用作用域链
在下面的例子中,如果要在bar函数中查询变量a,则直接使用LHS查询,赋值为100即可
var a = 1;
var b = 2;
function fn(x){
var a = 10;
function bar(x){
var a = 100;
b = x + a;
return b;
}
bar(20);
bar(200);
}
fn(0);
【2】如果自身作用域中未声明该变量,则需要使用作用域链进行查找
这时,就引出了另一个概念——自由变量。在当前作用域中存在但未在当前作用域中声明的变量叫自由变量
在下面的例子中,如果要在bar函数中查询变量b,由于b并没有在当前作用域中声明,所以b是自由变量。bar函数的作用域链是bar -> fn -> 全局。到上一级fn作用域中查找b没有找到,继续到再上一级全局作用域中查找b,找到了b
var a = 1;
var b = 2;
function fn(x){
var a = 10;
function bar(x){
var a = 100;
b = x + a;
return b;
}
bar(20);
bar(200);
}
fn(0);
[注意]如果标识符没有找到,则需要分为RHS和LHS查询进行分析,若进行的是LHS查询,则在全局环境中声明该变量,若是严格模式下的LHS查询,则抛出ReferenceError(引用错误)异常;若进行的是RHS查询,则抛出ReferenceError(引用错误)异常。详细情况移步至此
【执行环境】
执行环境(execution context),有时也称为执行上下文、执行上下文环境或环境,定义了变量或函数有权访问的其他数据。每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中
一定要区分执行环境和变量对象。执行环境会随着函数的调用和返回,不断的重建和销毁。但变量对象在有变量引用(如闭包)的情况下,将留在内存中不被销毁
这是例子中的代码执行到第15行时fn(0)函数的执行环境,执行环境里的变量对象保存了fn()函数作用域内所有的变量和函数的值
【执行流】
代码的执行顺序叫做执行流,程序源代码并不是按照代码的书写顺序一行一行往下执行,而是和函数的调用顺序有关
例子中的执行流是第1行 -> 第2行 -> 第4行 -> 第15行 -> 第5行 -> 第7行 -> 第12行 -> 第8行 -> 第9行 -> 第10行 -> 第11行 -> 第13行 -> 第8行 -> 第9行 -> 第10行 -> 第11行 -> 第14行
[注意]在程序代码执行之前存在着编译和声明提升(hoisting)的过程,本例中假设代码是已经经过声明提升过程之后的代码
【执行环境栈】
执行环境栈类似于作用域链,有序地保存着当前程序中存在的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。javascript程序中的执行流正是由这个机制控制着
在例子中,当执行流进入bar(20)函数时,当前程序的执行环境栈如下图所示,其中黄色的bar(20)执行环境表示当前程序正处此执行环境中
当bar(20)函数执行完成后,当前程序的执行环境栈如下图所示,bar(20)函数的执行环境被销毁,等待垃圾回收,控制权交还给黄色背景的fn(0)执行环境
说明
下面按照代码执行流的顺序对该图示进行详细说明
【1】代码执行流进入全局执行环境,并对全局执行环境中的代码进入声明提升(hoisting)
总结
【1】javascript使用的是词法作用域。对于函数来说,词法作用域是在函数定义时就已经确定了,与函数是否被调用无关。通过作用域,可以知道作用域范围内的变量和函数有哪些,却不知道变量的值是什么。所以作用域是静态的
[注意]通过eval()函数和with语句可以对作用域进行动态修改
【2】对于函数来说,执行环境是在函数调用时确定的,执行环境包含作用域内所有变量和函数的值。在同一作用域下,不同的调用(如传递不同的参数)会产生不同的执行环境,从而产生不同的变量的值。所以执行环境是动态的
深入理解javascript作用域系列第五篇——一张图理解执行环境和作用域的更多相关文章
- 深入理解javascript函数系列第三篇——属性和方法
× 目录 [1]属性 [2]方法 前面的话 函数是javascript中的特殊的对象,可以拥有属性和方法,就像普通的对象拥有属性和方法一样.甚至可以用Function()构造函数来创建新的函数对象.本 ...
- 深入理解javascript函数系列第三篇
前面的话 函数是javascript中特殊的对象,可以拥有属性和方法,就像普通的对象拥有属性和方法一样.甚至可以用Function()构造函数来创建新的函数对象.本文是深入理解javascript函数 ...
- 前端工程师技能之photoshop巧用系列第五篇——雪碧图
× 目录 [1]定义 [2]应用场景 [3]合并[4]实现[5]维护 前面的话 前面已经介绍过,描述性图片最终要合并为雪碧图.本文是photoshop巧用系列第五篇——雪碧图 定义 css雪碧图(sp ...
- 深入理解javascript作用域系列第五篇
前面的话 对于执行环境(execution context)和作用域(scope)并不容易区分,甚至很多人认为它们就是一回事,只是高程和犀牛书关于作用域的两种不同翻译而已.但实际上,它们并不相同,却相 ...
- javascript运动系列第五篇——缓冲运动和弹性运动
× 目录 [1]缓冲运动 [2]弹性运动 [3]距离分析[4]步长分析[5]弹性过界[6]弹性菜单[7]弹性拖拽 前面的话 缓冲运动指的是减速运动,减速到0的时候,元素正好停在目标点.而弹性运动同样是 ...
- 深入理解javascript对象系列第三篇——神秘的属性描述符
× 目录 [1]类型 [2]方法 [3]详述[4]状态 前面的话 对于操作系统中的文件,我们可以驾轻就熟将其设置为只读.隐藏.系统文件或普通文件.于对象来说,属性描述符提供类似的功能,用来描述对象的值 ...
- 深入理解javascript函数系列第四篇——ES6函数扩展
× 目录 [1]参数默认值 [2]rest参数 [3]扩展运算符[4]箭头函数 前面的话 ES6标准关于函数扩展部分,主要涉及以下四个方面:参数默认值.rest参数.扩展运算符和箭头函数 参数默认值 ...
- javascript动画系列第五篇——模拟滚动条
× 目录 [1]原理介绍 [2]数字加减 [3]元素尺寸[4]内容滚动 前面的话 当元素内容溢出元素尺寸范围时,会出现滚动条.但由于滚动条在各浏览器下表现不同,兼容性不好.所以,模拟滚动条也是很常见的 ...
- javascript面向对象系列第五篇——拖拽的实现
前面的话 在之前的博客中,拖拽的实现使用了面向过程的写法.本文将以面向对象的写法来实现拖拽 写法 <style> .test{height: 50px;width: 50px;backgr ...
随机推荐
- jsp使用EL表达式回传boolean值出错的问题
在最近做的一个项目中使用session回传的属性中有一个为boolean,报出错. 属性名字为"isAdmit",布尔类型.后来我上网查了一下,是因为我使用了Myeclipse的自 ...
- VS2015 调试时 编辑并继续不可用
最近在项目中遇到一次调试时 编辑并继续不可用.结合网上说的工具->设置->调试->常规下的一些操作,到后来还是不可用,最后把项目的解决方案平台改成Mixed Platform ,之后 ...
- iOS tableview自定义cell上添加按钮实现删除功能
在删除的时候,先删除数据源,再删除cell 但是,会发现一直崩: numberOfRowsInSection 解决方案:
- 【SQL语句】update ... ... from ......
要求:修改vaj表中的vaj02字段的值,vaj02字段的值=cag.cag03的值,vaj 表与 cag 表无直接关联 实现: update vaj set vaj02=c.cag03 from l ...
- Ansible-playbook批量部署,更新war脚本,可以再完善----后续再update
- name: install tomcat admin hosts: all sudo: True vars: war_file: /root/test.war tomcat_root: /data ...
- json相关的一些用法
一. json可以表示3种类型的值: 简单值 . 对象. 数组 表示对象时:>1. 没有变量的概念 ,所以不用申明变量 >2. 没有末尾结束 ...
- JavaIO 将数据写入到文件中去
package com.Practice_FileWriter; import java.io.FileWriter; import java.io.IOException; public class ...
- iOS8沙盒路径的变化
iOS8中的的沙盒路径发生了变化 之前是这样的路径,通过NSHomedictionary()获取的家路径 /Users/wupeng/Library/Application Support/iPhon ...
- 【luogu】 P1880 石子合并
原题原题原题原题原题 先贴上错误代码... ↓错误代码↓ #include <iostream> #include <cstdio> #include <cstring& ...
- 一鼓作气 博客--第五篇 note5
一.迭代器 二.装饰器 三.生成器 1.生成列表的方式有几种 2.把列表每个数都加1 2.1 data =[1,2,3] for i in map(lambda x:x+1,data):print(i ...