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 相关文章推荐
自适应图片大小的弹出窗口
Jul 27 Javascript
一个加密JavaScript的开源工具PACKER2.0.2
Nov 04 Javascript
9个javascript语法高亮插件 推荐
Jul 18 Javascript
jquery隐藏标签和显示标签的实例
Nov 11 Javascript
8个实用的jQuery技巧
Mar 04 Javascript
jquery小火箭返回顶部代码分享
Aug 19 Javascript
JS图片左右无缝隙滚动的实现(兼容IE,Firefox 遵循W3C标准)
Sep 23 Javascript
AngularJS实现的回到顶部指令功能实例
May 17 Javascript
利用 spin.js 生成等待效果(js 等待效果)
Jun 25 Javascript
微信小程序按钮去除边框线分享页面功能
Aug 27 Javascript
vue 插件的方法代码详解
Jun 06 Javascript
elementui之el-tebs浏览器卡死的问题和使用报错未注册问题
Jul 06 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
destoon实现调用热门关键字的方法
2014/07/15 PHP
Jquery 的扩展方法总结
2011/10/01 Javascript
javascript中数组的冒泡排序使用示例
2013/12/18 Javascript
jQuery插件开发的两种方法及$.fn.extend的详解
2014/01/16 Javascript
jQuery html()方法使用不了无法显示内容的问题
2014/08/06 Javascript
jQuery将多条数据插入模态框的示例代码
2014/09/25 Javascript
JS给Textarea文本框添加行号的方法
2015/08/20 Javascript
bootstrap和jQuery.Gantt的css冲突 如何解决
2016/05/29 Javascript
jQuery实现对象转为url参数的方法
2017/01/11 Javascript
微信小程序 安全包括(框架、功能模块、账户使用)详解
2017/01/16 Javascript
nodejs集成sqlite使用示例
2017/06/05 NodeJs
JavaScript门面模式详解
2017/10/19 Javascript
JS实现在文本指定位置插入内容的简单示例
2017/12/22 Javascript
使用react context 实现vue插槽slot功能
2019/07/18 Javascript
javascript 原型与原型链的理解及应用实例分析
2020/02/10 Javascript
[06:57]DOTA2-DPC中国联赛 正赛 Ehome vs PSG.LGD 选手采访
2021/03/11 DOTA
python如何实现int函数的方法示例
2018/02/19 Python
解决pip install的时候报错timed out的问题
2018/06/12 Python
Django中日期处理注意事项与自定义时间格式转换详解
2018/08/06 Python
教你一步步利用python实现贪吃蛇游戏
2019/06/27 Python
使用PyCharm进行远程开发和调试的实现
2019/11/04 Python
python manage.py runserver流程解析
2019/11/08 Python
在OpenCV里使用Camshift算法的实现
2019/11/22 Python
Python3 中作为一等对象的函数解析
2019/12/11 Python
python 使用raw socket进行TCP SYN扫描实例
2020/05/05 Python
html5 canvas-1.canvas介绍(hello canvas)
2013/01/07 HTML / CSS
国际象棋商店:The Chess Store
2018/07/09 全球购物
正宗的日本零食和糖果订阅盒:Bokksu
2019/11/21 全球购物
简述进程的启动、终止的方式以及如何进行进程的查看
2013/07/12 面试题
亿企通软件测试面试题
2012/04/10 面试题
简历中个人自我评价范文
2013/12/26 职场文书
竟聘演讲稿范文
2013/12/31 职场文书
写演讲稿要注意的六件事
2014/01/14 职场文书
学雷锋标语
2014/06/25 职场文书
教师工作能力自我评价
2015/03/04 职场文书
Python虚拟环境virtualenv是如何使用的
2021/06/20 Python