JS之执行上下文
执行上下文(execution context),是JS中的一个很重要的概念。它对于我们理解函数定义,执行时都做了什么有着很大的意义。理解它我们才能明白我们常说的函数声明提升,作用域链,闭包等原理。
在解释之前,我们先来看看经常会看到这样一段代码。
console.log(a); //undefined
var a =1;
这段代码我们都知道原因,也就是变量声明提升。
再看下面一段代码
var a=10;
function b(){
console.log(a);//undefiend
var a=20;
}
b();
可能会有人不明白,好像按照作用域链查找应该会是10而为什么还是undefine呢?要去解释这个问题,要去理解为什么变量会提升,我们还是要回到执行上下文中。
执行上下文存在于两种情景中,一段script脚本或者一个函数中。一段script脚本中,有全局上下文环境,在这个时候进行了变量的定义与函数的声明。在一个函数上下文环境中发生了什么,在下文中有分析。
每一个执行上下文环境,都有一个与之对应的变量对象(variable object),我们也简称为VO。这个VO保存了一个执行上下文环境定义的所有的变量和函数。
VO:{
变量,
函数,
arguments对象,
参数
}.
一.上下文的创建过程发生了什么?
上下文的创建过程可以分为两个阶段。
1.上下文的建立阶段,即在一个函数被调用但是在执行该函数内部代码之前的这段时间。在这段时间中,上下文做了如下几件事。
①.建立函数,变量,arguments对象,参数。在这个时候除了arguments,函数声明,以及参数已被赋值,其他的变量属性都默认是undefined。如果没有传入参数,它也是undefined。
②建立作用域链,注意是作用域链而不是作用域。这是因为函数的作用域在定义它的时候就已经确定了。
③.确定this的值。
2.代码的执行阶段。
①.变量赋值
②.函数引用
③.执行其它的代码。
其实,这个时候把执行上下文理解成一个对象更直观。
EC_Object={
VO(变量对象):函数中的arguments对象,参数,内部变量以及函数,
chain(作用域链):VO以及所有执行上下文中的VO,
this:{}
}。
二.函数调用时发生了什么?
每一个函数在被调用时,都有执行代码前,执行代码时,还有执行后这三个阶段。
①.执行代码前
1.创建执行上下文,每一个函数在被调用时都会产生一个新的执行上下文环境。
2.进入创建阶段。
(1).建立VO对象
①.建立arguments对象,检查当前上下文中的参数。如果传入参数则给参数赋值,否则参数值为undefined。
②.检查当前上下文中的函数声明。每找到一个函数声明,就在VO下建立一个该函数名为属性名的属性值,该属性值就是指向该函数在内存中的地址的一个引用。如果函数名已经存在于VO中,则对应的属性值会被新的引用所覆盖。
③.检查当前上下文中的变量声明。每找到一个变量声明,就在VO下建立一个该变量名为属性名的属性值,属性值默认为undefied(注意:此时还没有赋值)。如果该变量名存在于VO中,则会直接跳过而不会覆盖。
function f(){
var a=10;
function a(){};
}
f();
VO:{
a:function(){},//函数名覆盖了变量名
arguments,
}
function f(){
function a(){};
var a=10;
}
f();
VO:{
a:function(){},//变量名跳过了
arguments,
}
(2)初始化作用域链。作用域中变量的值是在执行过程中产生而确定的,作用域却是在函数创建时就确定的。若要查找一个作用域下的某个变量的值,就要找到这个作用域对象的执行上下文,找到它的VO,再到其中去寻找变量的值。如果在该上下文环境没有找到,他会随着作用域链向上查找另一个上下文环境,找到该环境下的VO,直到全局环境,全局上下文环境中的VO始终是作用域链中的最后一个对象。
(3)确定上下文中this的指向
②.执行代码时
执行函数内部代码时,一步步运行,给VO中的变量属性赋值。
③.执行代码后
当一个执行上下文环境中的代码执行完毕后,该环境会被销毁,保存在其中的所有变量和函数都会随之销毁。但是,有一个特殊情况,则是另一个我要去说明的问题——闭包。
了解了这么多。我们再回到之前提到的两段代码。
console.log(a);//undefiend
var a=1; //这个时候是全局执行环境,在执行代码前,该环境已经为a定义了,全局中的VO已经有了a这个变量属性,值默认为undefined。
因为代码是一行行进行的,所有第一行代码执行后,a其实已经定义了,值为undefined。这就是所谓的变量声明提升了。
第二段代码
var a=10;
function b(){
console.log(a);//undefiend
var a=20;
}
b();
//在调用b函数,执行代码之前。创建了一个b函数的执行上下文环境。在该环境下,VO中也已经有了a这个变量属性,值是undefined,
此时在执行代码第一句时,已经在这个函数执行上下文中的VO中找到了a,而不用再到全局中的VO中去找。所有仍然是undefined。
这就是所谓的变量查找就近原则,是不是很好理解。
关于上下文的说明就这么多,如果发现问题,还希望大家能帮我及时纠正,多多交流。
关于作用域与闭包的问题,可以看下我的另一篇文章。
JS之执行上下文的更多相关文章
- js高级-执行上下文
全局上下文 方法1() 压入 (栈的数据结构 先进后出)push() pop() 1.当一个函数在调用另外一个函数的时候新调用的函数会行成一个新的执行上下文 压入执行环境栈的栈顶 2.浏览器js执 ...
- JS的执行上下文
定义 执行上下文时是代码执行时的环境,JS代码在运行前进行编译,那么会生成两部分,一部分是可执行的代码,而另一部分则是执行上下文. 发展 执行上下文所包含的内容是在不断的变化的.它主要分为了三个不同的 ...
- 一文弄懂js的执行上下文与执行上下文栈
目录 执行上下文与执行上下文栈 变量提升与函数提升 变量提升 函数提升 变量提升与函数提升的优先级 变量提升的一道题目引出var关键字与let关键字各自的特性 执行上下文 全局执行上下文 函数(局部) ...
- 进阶学习js中的执行上下文
在js中的执行上下文,菜鸟入门基础 这篇文章中我们简单的讲解了js中的上下文,今天我们就更进一步的讲解js中的执行上下文. 1.当遇到变量名和函数名相同的问题. var a = 10; functio ...
- 深入理解JavaScript执行上下文、函数堆栈、提升的概念
本文内容主要转载自以下两位作者的文章,如有侵权请联系我删除: https://feclub.cn/post/content/ec_ecs_hosting http://blog.csdn.net/hi ...
- 深入理解js——执行上下文
什么是"执行上下文"?暂且不下定义,先看一段代码: 第一句报错,a未定义,很正常.第二句.第三句输出都是undefined,说明浏览器在执行console.log(a)时,已经知道 ...
- js执行上下文(由浅入深)
每一个函数都有自己的执行上下文EC(执行环境 execution context),并且每个执行上下文中都有它自己的变量对象VO(Variable object),用于存储执行上下文中的变量 .函数声 ...
- 深入学习JS执行--创建执行上下文(变量对象,作用域链,this)
一.介绍 本篇继上一篇深入理解js执行--单线程的JS,这次我们来深入了解js执行过程中的执行上下文. 本篇涉及到的名词:预执行,执行上下文,变量对象,活动对象,作用域链,this等 二.预执行 在上 ...
- Js 作用域与作用域链与执行上下文不得不说的故事 ⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄
最近在研究Js,发现自己对作用域,作用域链,活动对象这几个概念,理解得不是很清楚,所以拜读了@田小计划大神的博客与其他文章,受益匪浅,写这篇随笔算是自己的读书笔记吧~. 作用域 首先明确一个概念,js ...
随机推荐
- REST面向资源架构 RESTful架构
REST基础概念: 在REST中的一切都被认为是一种资源. 每个资源由URI标识. 使用统一的接口.处理资源使用POST,GET,PUT,DELETE操作类似创建,读取,更新和删除(CRUD)操作. ...
- 深入理解.net remoting 与webservice
1. .NET Remoting .NET Remoting是微软随.NET推出的一种分布式应用解决方案,被誉为管理应用程序域之间的 RPC 的首选技,它允许不同应用程序域之间进行通信(这里的通信可以 ...
- ES增删改查入门1
1.RESTful接口使用方法 为了方便直观我们使用Head插件提供的接口进行演示,实际上内部调用的RESTful接口. RESTful接口URL的格式: http://localhost:9200/ ...
- 使用Aspose.Cell控件实现Excel高难度报表的生成
1.使用Aspose.Cell控件实现Excel高难度报表的生成(一) http://www.cnblogs.com/wuhuacong/archive/2011/02/23/1962147.html ...
- 如何解读IL代码
如何解读IL代码 关于IL代码,我有将从三个方面去揭开它神秘的面纱.IL代码是什么?我们为什么要去读懂IL代码?我们如何去读懂IL代码?这三个问题的解答,将是我解读IL代码的整体思路. IL代码是什么 ...
- Swift-取消传统For循环
1.取消传统的For循环 传统的for,在swift 3.0 被取消 i++/++i在swift 3.0 被取消 i += 1代替 for var i = 0;i<10;i +=1 { } 2. ...
- SSL、数字签名、CA 工作原理
SSL.数字签名.CA 工作原理 对称加密和非对称加密介绍和区别 什么是对称加密技术? 对称加密采用了对称密码编码技术,它的特点是文件加密和解密使用相同的密钥,即加密密钥也可以用作解密密钥,这种方 ...
- Python常用模块(二)
一.json与pickle json与pickle模块是为了完成数据的序列化. 序列化是指把对象(变量)从内存中变成可存储或传输的过程,在Python中叫picking,在其他语言中也由其他的叫法,但 ...
- 【^.^】hello world~~
一直以来都没有在公共博客上写作的习惯,加之Evernote的强大和方便好用,让我仅仅依赖它就足以满足日常学习笔记的记录和整理. 不过看着Evernote里面记录的大大小小的笔记已经有400+了,觉得应 ...
- ListView、DataGrid 不显示列标题
<!--ListView不显示列标题--> <Style TargetType="{x:Type GridViewColumnHeader}"> <S ...