javascript 作用域链与执行环境
前言:这是笔者学习之后自己的理解与整理。如果有错误或者疑问的地方,请大家指正,我会持续更新!
作用域、作用域链、执行环境、执行环境栈以及 this 的概念在 javascript 中非常重要,本人经常弄混淆,这里梳理一下:
- 局部作用域函数内部的区域,全局作用域就是 window;
- 作用域链取决于函数被声明时的位置,解析标识符的时候就先找当前作用域,再向外查找,直到全局,这样一个顺序;和函数在哪里调用无关;
- 执行环境就是函数可访问的数据和变量的集合,也就是函数的作用域链上的所有数据和变量;
- 执行环境栈就是根据代码执行顺序,各执行环境按照栈的形式逐层访问,并且用完了退出来扔掉;如果当前执行环境(存放当前作用域链里的数据和变量)找不到变量,那就是找不到了,不会往之前的那个执行环境查找,它和作用域链是不同的;
作用域
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值类型与执行环境和垃圾处理机制
JavaScript变量分为基本值类型和引用值类型,基本值类型就是以下这五种:Boolean,Number,String,Null,Undefined.基本值类型和引用值类型具有以下特点: 1.基本值 ...
- JavaScript作用域链详解
JavaScript的作用域链还是很有味道的,搞懂了这个知识点,闭包的问题也就迎刃而解咯 1.JavaScript的全局变量和局部变量 首先,先来看看js的全局变量和局部变量,js不是块级作用域,所以 ...
- JavaScript作用域链的理解
前言 作用域是JavaScript一个很重要的概念,想要学好JavaScript就需要理解javascript作用域和作用域链的工作原理.这篇文章对JavaScript作用域链和作用域链做一个简单的介 ...
- JavaScript 作用域链图具体解释
<script type="text/javascript"> /** * 作用域链: */ var a = "a"; function hao94 ...
- javascript中函数的执行环境、作用域链、变量对象与活动对象
javascript高级程序设计中:对执行环境.作用域链.变量对象.活动对象的解释: 1.执行环境: 执行环境:有时也叫环境:是JavaScript中最为重要的一个概念:执行环境定义了变量或函数有权访 ...
- javascript 作用域链及闭包,AO,VO,执行环境
下面的文章内容会根据理解程度不断修正. js变量作用域: 定义:变量在它申明的函数体以及函数体内嵌套的任意函数体内有定义. function AA(){ var bb='我是AA内部变量'; func ...
- JavaScript之JS的执行环境和作用域
一.执行环境是JavaScript中最为重要的一个概念.执行环境定义了变量或函数有权访问的其他数据,决定了他们各自的行为,每个执行环境都有一个与之关联的变量对象(variable object),环境 ...
- JavaScript中变量、执行环境、作用域与C#中的异同
首先需要明确一个执行环境的概念,执行环境这个概念是用来理解作用域的,在js中,执行环境分为全局执行环境和局部(function)执行环境,而在C#这类的C类语言中,还有一个块级别的执行环境,如if语句 ...
- JavaScript作用域链
之前写过一篇JavaScript 闭包究竟是什么的文章理解闭包,觉得写得很清晰,可以简单理解闭包产生原因,但看评论都在说了解了作用域链和活动对象才能真正理解闭包,起初不以为然,后来在跟公司同事交流的时 ...
随机推荐
- 前端的UI设计与交互之数据录入篇
数据录入是获取对象信息的重要交互方式,用户会频繁的增加.修改或删除信息.多种多样的文本录入和选择录入方式帮助用户更加清晰和高效的完成这项体验.设计者应当注意这几点:为初级用户/偶尔访问的用户提供简单易 ...
- express+mongodb+socket.io
node后端代码 // Setup basic express server var express = require('express'); var app = express(); var pa ...
- ArrayList 源码分析
ArrayList 源码分析 1. 结构 首先我们需要对 ArrayList 有一个大致的了解就从结构来看看吧. 1. 继承 该类继承自 AbstractList 这个比较好说 2. 实现 这 ...
- Bootstrap3 datetimepicker控件的使用
Bootstrap3 日期+时间选择控件 1.支持日期选择,格式设定 2.支持时间选择 3.支持时间段选择控制 4.支持中文 官网地址:http://eonasdan.github.io/bootst ...
- Algorithm --> 爬楼梯求最大分数
爬楼梯求最大分数 如下图,最大分数是: 10+20+25+20=75. 要求: 1.每次只能走一步或者两步: 2.不能连续三步走一样的,即最多连续走两次一步,或者连续走两次两步: 3.必 ...
- Oracle安装11.2.0.4.180116补丁及如何检查数据库安装补丁
最近做了一个安装11.2.0.4.180116补丁的实验,突然想起之前和同事讨论的一个问题:如何检查数据库安装补丁的版本,之前搜到的是去查dba_registry_history,有的说在操作系统中执 ...
- Linux环境下Swap配置方法
Linux环境下Swap配置方法 场景: 今天下午安装一个CentOS6.5操作系统,忘记配置swap分区.看看如何安装系统之后,增加和删除swap分区.方法如下:1.内存占用情况[root@josh ...
- C#,一份超简单的数据库帮助类,SqlHelp
简单,实用,留存. using System; using System.Collections.Generic; using System.Configuration; using System.D ...
- alpha冲刺第三天
一.合照 二.项目燃尽图 三.项目进展 今天是一个瓶颈期,在昨天被困住的地方今天还是没能解决,所以今天的项目进展并没有发生什么变化. 今天晚上xl和lj去实验室找学姐了,在学姐的帮助下大概有了一点思路 ...
- C语言博客作业—函数嵌套调用
一.实验作业 1.1 PTA题目:递归法对任意10个数据按降序排序 1.1.1设计思路 void sort(int a[],int n) { 定义整型循环变量i,中间变量temp,最小值min: 令m ...