javascript作用域链与执行环境详解


Posted in Javascript onMarch 25, 2017

前言:这是笔者学习之后自己的理解与整理。如果有错误或者疑问的地方,请大家指正,我会持续更新!

作用域、作用域链、执行环境、执行环境栈以及this的概念在javascript中非常重要,本人经常弄混淆,这里梳理一下;

  • 局部作用域函数内部的区域,全局作用域就是window;
  • 作用域链取决于函数被声明时的位置,解析标识符的时候就先找当前作用域,再向外查找,直到全局,这样一个顺序;和函数在哪里调用无关;
  • 执行环境就是函数可访问的数据和变量的集合,也就是函数的作用域链上的所有数据和变量;
  • 执行环境栈就是根据代码执行顺序,各执行环境按照栈的形式逐层访问,并且用完了退出来扔掉;如果当前执行环境(存放当前作用域链里的数据和变量)找不到变量,那就是找不到了,不会往之前的那个执行环境查找,它和作用域链是不同的;
  • this是一个对象,它取决于是谁执行的,谁执行那就是谁;(this的概念还是不太清楚,这里写的有点万金油,过两天再来修正) 

作用域

JavaScript没有块级作用域的概念,只有函数级作用域:变量在声明它们的函数体及其子函数内是可见的。

作用域就是变量和函数的可访问范围,控制着变量和函数的可见性与生命周期,在JavaScript中变量的作用域有全局作用域和局部作用域。

变量没有在函数内声明或者声明的时候没有带var就是全局变量,拥有全局作用域;

<script type="text/javascript">
 function test1(){
  a = 1;//全局变量,只有在当前函数运行时,才有效
  }
 test1();
 console.log(a);//1 注意test1函数必须运行,不然找不到a
 </script>

全局变量可以当做window对象的属性用,他们是一样的; 

<script type="text/javascript"> 
 var b = 1;//全局变量
 console.log(b === window.b);//true 全局变量可以当做window对象的属性用,他们是一样的;
</script>

window对象的所有属性拥有全局作用域,在代码任何地方都可以访问;

函数内部声明的变量就是局部变量,只能在函数体内使用,函数的参数虽然没有使用var但仍然是局部变量。

<script type="text/javascript"> 
   var c = 1;//全局变量
//   console.log(d);//ReferenceError: d is not defined 引用错误,当前作用域就是最外层作用域,依然找不到d
   function test2(d){
    console.log(c);//1 全局变量,哪都可以访问;(先找当前作用域,找不到,就向外层作用域找,直到window最外层,找到了)
    console.log(d);//3 形参是局部变量,只有当前作用域下可以访问
   }
   test2(3);
  </script>

作用域链

作用域链取决于函数被声明时的位置,解析标识符的时候就先从当前作用域开始找,在当前作用域中无法找到时,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量,或抵达最外层的作用域(也就是全局作用域)为止;它的路线已经被定死了,和函数在哪里运行无关;

<script type="text/javascript">
   var a = 1;
   var b = 2;
   var c = 3;
   var d = 4;
   function inner(d) {//它的作用域链是inner---全局
    var c = 8;
    console.log(a);//1 当前作用域找不到a,去全局作用域找到了a=1
    console.log(b);//2 当前作用域找不到b,去全局作用域找到了b=2
    console.log(c);//8 当前作用域找到了c=8
    console.log(d);//7 当前作用域找到了d=7,形参也是局部作用域
   // console.log(e);//ReferenceError: e is not defined 引用错误,找不到e, 它的作用域链是inner---全局
    console.log(a+b+c+d);//18
   }
   function outter(e) {
    var a = 5;//inner()的作用域链是inner---全局,所以这个a相当于无效
    var b = 6;//inner()的作用域链是inner---全局,所以这个a相当于无效
    inner(7);
   }
   outter(999);//这个999无效,里面的e根本找不到
</script>

在多层的嵌套作用域中可以定义同名的标识符,这叫作“遮蔽效应”,内部的标识符“遮蔽”了外部的标识符

通过window.a这种技术可以访问那些被同名变量所遮蔽的全局变量。但非全局的变量如果被遮蔽了,无论如何都无法被访问到;

<script type="text/javascript">
   var a = 'Lily';
   var b = 'Lucy';
   function outer() {
    var b = 'Jesica';
    var c = 'Susan';
    function inner(c) {
     console.log(a);//Lily 
     console.log(window.b);//Lucy
     console.log(b);//Jesica
     console.log(c);//Jenifer
    }
    inner('Jenifer');
   }
   outer();
</script>

执行环境

执行环境(execution context),也叫执行上下文。每个执行环境都有一个变量对象(variable object),保存函数可访问的所有变量和数据(也就是函数的作用域链上的所有数据和变量)。我们的代码访问不到它,它是给引擎使用的;

执行环境栈,当执行进入一个函数时,函数的执行环境就会被推入一个栈中。而在函数执行完之后,栈将其执行环境移除,它里面的变量和数据会被标记清除,等待垃圾回收,再把控制权返回给之前的执行环境。javascript程序中的执行正是由这个机制控制着;

需要注意的是如果当前执行环境(存放当前作用域链里的数据和变量)找不到变量,那就是找不到了,不会往之前的那个执行环境查找,和作用域链是不一样的;

代码的执行顺序也不全是一行一行的执行,而是和函数的调用顺序有关:

  • 代码进入全局执行环境,全局执行环境放入环境栈;
  • 当执行到一个函数时,就把这个函数的执行环境推入到环境栈顶端,之前的执行环境往后;
  • 全局执行环境最先进入,所以一直在底端;就和栈的概念差不多;
  • 函数执行完之后,再把它的执行环境从作用域链顶端移除,它保存的数据和函数都被标记清除,等待垃圾回收;
  • 控制权交给之前的执行环境,继续往下执行;
  • 当页面关闭时,全局执行环境才销毁;
<script type="text/javascript">
 var a = 1;
 var b = 2;
 var c = 3;
 var d = 4;
 function inner(d) {//它的作用域链是inner---全局
  var c = 8;
  console.log(a);//1 当前作用域找不到a,去全局作用域找到了a=1
  console.log(b);//2 当前作用域找不到b,去全局作用域找到了b=2
  console.log(c);//8 当前作用域找到了c=8
  console.log(d);//7 当前作用域找到了d=7,形参也是局部作用域
 // console.log(e);//ReferenceError: e is not defined 引用错误,找不到e, 它的作用域链是inner---全局
  console.log(a+b+c+d);//18
 }
 function outter(e) {
  var a = 5;//inner()的作用域链是inner---全局,所以这个a相当于无效
  var b = 6;//inner()的作用域链是inner---全局,所以这个a相当于无效
  inner(7);
 }
 outter(999);//这个999无效,里面的e根本找不到
</script>

以上代码的执行顺序:

代码执行进入全局执行环境,并对全局执行环境中的代码进入声明提升;

执行第2行,赋值a=1; 然后第3行赋值b=2; 然后第4行赋值c=3; 然后第5行赋值d=4;
执行第20行,调用outer(999)函数,然后进入outer(999)函数执行环境,声明提升,并将实参999传给形参e;现在环境栈中有两个执行环境,outer(999)是当前执行环境;
执行第16行,赋值a=5; 然后第17行赋值b=6;

执行第18行,调用inner(7)函数,然后进入inner(7)函数执行环境,声明提升,并将实参7传给形参d;
执行第7行,赋值c=8; 然后运算并输出; 

代码优化

由于在作用域链上查找变量是需要消耗性能的,我们应该尽快的找到变量,所以在函数多层嵌套的时候,我们应尽可能的使用函数内部的局部变量;

我们在函数内部使用全局变量可以说是一种跨作用域操作,如果某个跨作用域的值在函数的内部被多次使用,那么我们就把它存储到局部变量里,这样可以提高性能。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
基于jQuery的实现简单的分页控件
Oct 10 Javascript
兼容各大浏览器的JavaScript阻止事件冒泡代码
Jul 09 Javascript
jquery.qtip提示信息插件用法简单实例
Jun 17 Javascript
vue.js 上传图片实例代码
Jun 22 Javascript
ionic2屏幕适配实现适配手机、平板等设备的示例代码
Aug 11 Javascript
Vue多种方法实现表头和首列固定的示例代码
Feb 02 Javascript
微信小程序使用form表单获取输入框数据的实例代码
May 17 Javascript
微信小程序实现收货地址左滑删除
Nov 18 Javascript
JavaScript简易计算器制作
Jan 17 Javascript
jQuery 隐藏/显示效果函数用法实例分析
May 20 jQuery
Vue中登录验证成功后保存token,并每次请求携带并验证token操作
Sep 08 Javascript
微信小程序input抖动问题的修复方法
Mar 03 Javascript
vue中用动态组件实现选项卡切换效果
Mar 25 #Javascript
使用vue.js写一个tab选项卡效果
Mar 25 #Javascript
JavaScript 实现 Tab 点击切换实例代码
Mar 25 #Javascript
JS操作xml对象转换为Json对象示例
Mar 25 #Javascript
javascript实现的图片预览功能
Mar 25 #Javascript
JS控件bootstrap suggest plugin使用方法详解
Mar 25 #Javascript
bootstrap table动态加载数据示例代码
Mar 25 #Javascript
You might like
目录,文件操作详谈―PHP
2006/11/25 PHP
openflashchart 2.0 简单案例php版
2012/05/21 PHP
在PHP中运行Linux命令并启动SSH服务的例子
2014/06/12 PHP
PHP的JSON封装、转变及输出操作示例
2019/09/27 PHP
tp5.1 框架数据库常见操作详解【添加、删除、更新、查询】
2020/05/26 PHP
Convert Seconds To Hours
2007/06/16 Javascript
关于jquery css的使用介绍
2013/04/18 Javascript
实现js保留小数点后N位的代码
2014/11/13 Javascript
如何解决谷歌浏览器下jquery无法获取图片的尺寸
2015/09/10 Javascript
基于JavaScript代码实现pc与手机之间的跳转
2015/12/23 Javascript
无需 Flash 使用 jQuery 复制文字到剪贴板
2016/04/26 Javascript
jQuery日程管理插件fullcalendar使用详解
2017/01/07 Javascript
原生JS实现圣旨卷轴展开效果
2017/03/06 Javascript
Node.js实现文件上传的示例
2017/06/28 Javascript
详解小程序毫秒级倒计时(适用于拼团秒杀功能)
2019/05/05 Javascript
JS字符串与二进制的相互转化实例代码详解
2019/06/28 Javascript
vue实现分页栏效果
2019/06/28 Javascript
JavaScript中BOM对象原理与用法分析
2019/07/09 Javascript
vue实现购物车结算功能
2020/06/18 Javascript
element-ui点击查看大图的方法示例
2020/12/14 Javascript
对于Python的Django框架使用的一些实用建议
2015/04/03 Python
python批量提取word内信息
2015/08/09 Python
python3+PyQt5+Qt Designer实现扩展对话框
2018/04/20 Python
浅谈Python 列表字典赋值的陷阱
2019/01/20 Python
python实现UDP协议下的文件传输
2020/03/20 Python
keras实现调用自己训练的模型,并去掉全连接层
2020/06/09 Python
css3实现简单的白云飘动背景特效
2020/10/28 HTML / CSS
HTML5 解析规则分析
2009/08/14 HTML / CSS
美国最大的珠宝首饰网上商城:Jewelry.com
2016/07/22 全球购物
Oakley官网:运动太阳镜、雪镜和服装
2016/09/30 全球购物
高中毕业生生活的自我评价
2013/12/08 职场文书
档案保密承诺书
2014/06/03 职场文书
新党章的学习心得体会
2014/11/07 职场文书
2014年政协工作总结
2014/12/09 职场文书
工作经历证明范本
2015/06/15 职场文书
Java实现给Word文件添加文字水印
2022/02/15 Java/Android