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 相关文章推荐
文字幻灯片
Jun 26 Javascript
分享8款优秀的 jQuery 加载动画和进度条插件
Oct 24 Javascript
js将iframe中控件的值传到主页面控件中的实现方法
Mar 11 Javascript
用JS将搜索的关键字高亮显示实现代码
Nov 08 Javascript
js数组去重的常用方法总结
Jan 24 Javascript
js识别不同浏览器基于userAgent做判断
Jul 29 Javascript
js实现最短的XML格式化工具实例
Mar 12 Javascript
浅析四种常见的Javascript声明循环变量的书写方式
Oct 14 Javascript
JS for...in 遍历语句用法实例分析
Aug 24 Javascript
ionic实现滑动的三种方式
Aug 27 Javascript
vue实现条件判断动态绑定样式的方法
Sep 29 Javascript
小程序click-scroll组件设计
Jun 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
短波收音机简介
2021/03/01 无线电
php smarty 二级分类代码和模版循环例子
2011/06/01 PHP
php数组函数序列之end() - 移动数组内部指针到最后一个元素,并返回该元素的值
2011/10/31 PHP
关于PHP的相似度计算函数:levenshtein的使用介绍
2013/04/15 PHP
php从数据库查询结果生成树形列表的方法
2015/04/17 PHP
javascript之bind使用介绍
2011/10/09 Javascript
使用ImageMagick进行图片缩放、合成与裁剪(js+python)
2013/09/16 Javascript
当某个文本框成为焦点时即清除文本框内容
2014/04/28 Javascript
吐槽一下我所了解的Node.js
2014/10/08 Javascript
jQuery结合AJAX之在页面滚动时从服务器加载数据
2015/06/30 Javascript
angularjs学习笔记之三大模块(modal,controller,view)
2015/09/26 Javascript
老生常谈 关于JavaScript的类的继承
2016/06/24 Javascript
AngularJS表单提交实例详解
2017/02/18 Javascript
vue组件实现文字居中对齐的方法
2017/08/23 Javascript
(模仿京东用户注册)用JQuery实现简单表单验证,初学者必看
2018/01/08 jQuery
基于纯JS实现多张图片的懒加载Lazy过程解析
2019/10/14 Javascript
JS实现简单tab选项卡切换
2019/10/25 Javascript
Vue项目vscode 安装eslint插件的方法(代码自动修复)
2020/04/15 Javascript
Vue实现简单的跑马灯
2020/05/25 Javascript
Python3基础之list列表实例解析
2014/08/13 Python
Python实现对比不同字体中的同一字符的显示效果
2015/04/23 Python
基于python的Paxos算法实现
2019/07/03 Python
Python交互式图形编程的实现
2019/07/25 Python
python中tkinter窗口位置\坐标\大小等实现示例
2020/07/09 Python
Bogner美国官网:滑雪服中的”Dior”
2018/01/30 全球购物
怎样声明接口
2014/09/19 面试题
银行贷款收入证明
2014/10/17 职场文书
家长高考寄语
2015/02/27 职场文书
工厂员工辞职信范文
2015/05/12 职场文书
入党群众意见范文
2015/06/02 职场文书
叶问观后感
2015/06/15 职场文书
法制教育讲座心得体会
2016/01/14 职场文书
《我的长生果》教学反思
2016/02/20 职场文书
2019商业计划书格式、范文
2019/04/24 职场文书
go select编译期的优化处理逻辑使用场景分析
2021/06/28 Golang
pytest实现多进程与多线程运行超好用的插件
2022/07/15 Python