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 相关文章推荐
Javascript 判断函数类型完美解决方案
Sep 02 Javascript
调试Node.JS的辅助工具(NodeWatcher)
Jan 04 Javascript
使用mini-define实现前端代码的模块化管理
Dec 25 Javascript
使用postMesssage()实现iframe跨域页面间的信息传递
Mar 29 Javascript
Angular 输入框实现自定义验证功能
Feb 19 Javascript
javascript DOM的详解及实例代码
Mar 06 Javascript
JavaScript通过filereader接口读取文件
May 10 Javascript
DatePickerDialog 自定义样式及使用全解
Jul 09 Javascript
基于Taro的微信小程序模板消息-获取formId功能模块封装实践
Jul 15 Javascript
js键盘事件实现人物的行走
Jan 17 Javascript
Vue如何使用混合Mixins和插件开发详解
Feb 05 Javascript
原生js实现的观察者和订阅者模式简单示例
Apr 18 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
PHP5.2下chunk_split()函数整数溢出漏洞 分析
2007/06/06 PHP
如何判断php数组的维度
2013/06/10 PHP
yii分页组件用法实例分析
2015/12/28 PHP
Laravel中间件实现原理详解
2016/10/09 PHP
PHP中SQL查询语句的id=%d解释(推荐)
2016/12/10 PHP
javascript创建数组之联合数组的使用方法示例
2013/12/26 Javascript
javascript检查浏览器是否支持flash的实现代码
2014/08/14 Javascript
jQuery的context属性用法实例
2014/12/27 Javascript
JavaScript中的原型链prototype介绍
2014/12/30 Javascript
Boostrap基础教程之JavaScript插件篇
2016/09/08 Javascript
jQuery实现页面顶部下拉广告
2016/12/30 Javascript
jQuery基于事件控制实现点击显示内容下拉效果
2017/03/07 Javascript
Node.JS文件系统解析实例详解
2017/05/15 Javascript
Vue resource中的GET与POST请求的实例代码
2017/07/21 Javascript
ionic3 懒加载
2017/08/16 Javascript
详解10分钟学会vue滚动行为
2017/09/21 Javascript
layui实现动态和静态分页
2018/04/28 Javascript
vue中npm包全局安装和局部安装过程
2019/09/03 Javascript
JS对日期操作封装代码实例
2019/11/08 Javascript
如何在sae中设置django,让sae的工作环境跟本地python环境一致
2017/11/21 Python
Python连接Mssql基础教程之Python库pymssql
2018/09/16 Python
Python面向对象编程基础实例分析
2020/01/17 Python
利用Python如何制作贪吃蛇及AI版贪吃蛇详解
2020/08/24 Python
详解pandas apply 并行处理的几种方法
2021/02/24 Python
Under Armour安德玛荷兰官网:美国高端运动科技品牌
2019/07/10 全球购物
PHP面试题及答案二
2015/05/23 面试题
介绍一下内联、左联、右联
2013/12/31 面试题
建筑工程专业学生的自我评价
2013/12/25 职场文书
买房子个人收入证明
2014/01/16 职场文书
腾讯广告词
2014/03/19 职场文书
2014年重阳节活动策划方案书
2014/09/16 职场文书
餐饮服务食品安全承诺书
2015/04/29 职场文书
老人节主持词
2015/07/04 职场文书
厉行节约工作总结
2015/08/12 职场文书
高端收音机+蓝牙音箱,JBL TUNER FM带收音蓝牙音箱评测
2021/04/24 无线电
MySQL范围查询优化的场景实例详解
2022/06/10 MySQL