javascript执行上下文和变量对象
执行上下文(execution context):
执行上下文就是当前 JavaScript 代码被解析和执行时所在环境的抽象概念。
js语言是一段一段的顺序执行,这个“段”其实就是我们说的这个执行上下文,分为:全局执行上下文,函数执行上下文,Eval函数执行上下文(很少用)。
执行上下文由以下几个属性构成:
executionContext:{
variable objects:var、function[、arguments]
scope chain:variable objects + all parents scope
thisValue:content object
}
执行上下文的代码分为两个阶段:
- 进入执行上下文
- 代码执行
进入执行上下文后初始化的规则如下(且按如下顺序执行):
- 函数的所有形参(这一条是在函数上下文中才用到):
- 由名称和对应值组成的一个变量对象的属性被创建
- 如果没有传实参,属性值将置为undefined
- 函数声明
- 由名称和该函数体组成的一个变量对象的属性被创建
- 如果有两个同名函数声明,后者会替换前者
- 变量声明:
- 由名称和undefined组成的一个变量对象的属性被创建
- 如果变量名称和已经声明的形参或者是函数名相同,则变量声明不会替代已经存在的这类属性
function test() {
console.log(a); // a is not defined
a = 1;
} test(); function test2() {
b = 1;
console.log(b); // 1,因为执行这句的时候b已经自动升级成了全局变量所以打印1
} test2();
例子1:执行test()报错是因为:没有var声明的变量不会发生变量提升!!
funA; // undefined
var funA = function () {
console.log('输出a1');
} funA(); // 输出a1 var funA = function () {
console.log('输出a2');
} funA(); // 输出a2
例子2主要是变量提升:
var funA;
var funA = ...
funA()
var funA = ...
funA()
预编译阶段先初始化得到var funA=undefined,所以第一个funA输出undefined;
然后顺序执行,先把function(){ console.log('输出a1') }赋值给funA,然后执行funA();
然后顺序执行,再用function(){ console.log('输出a2') }替换当前funA的值,然后再执行。
funA(); // 输出a2
function funA() {
console.log('输出a1');
} funA(); // 输出a2 function funA() {
console.log('输出a2');
} funA(); // 输出a2
例子3是函数提升:
function funA
funA()
funA()
funA()
预编译阶段初始化的时候解析到function,后面的funA会替换前面的,因此,这三个函数执行都执行的是后一个funA。
funA(); // 输出a2
var funA = function () {
console.log('输出a1');
} funA(); // 输出a1 function funA() {
console.log('输出a2');
} funA(); // 输出a1
例子4表示函数声明的优先级大于变量声明
var funA
function funA
funA() 【执行的是函数funA】
var funA = function(){}
funA() 【执行的是变量赋值后的funA】
funA() 【同上】
console.log(number); // ƒ number() {console.log('test')}
function number() {
console.log('test')
}
var number = 1; var number2 = 2;
console.log(number2); //
function number2() {
console.log('test')
} function number3(x) {
console.log(x); // ƒ x() { }
function x() { }
}
number3(5)
例子5
第一个demo是演示了函数提升和变量提升,但是由于function number()最先被提升,后面var number的提升会被忽略,所以第一个会输出函数体
第二个demo是因为预编译结束之后,直接给number2赋值,所以输出的是赋值后的number2
第三个demo说明函数声明的提升会覆盖函数参数。函数参数其实属于变量的一种形式,它的优先级最高,但是同样会受到函数声明的影响!
小结一下~
初始化规则是先处理函数声明,再处理变量声明
变量提升和函数提升通俗点说就是将变量和函数移动到代码顶,在创建阶段,js解释器会找到需要提升的变量和函数,并给他们在内存中开辟好空间,变量只声明并且赋值undefined,而函数会整个存入内存中!
在提升过程中,相同函数名的函数会覆盖前面的,函数提升会优先于变量提升
执行栈:
也称之为调用栈,是LIFO(后进先出)结构,用于存储在代码执行期间创建的所有执行上下文。
JavaScript引擎首次读取脚本时,首先将全局执行上下文push到当前执行栈,每当发生函数调用,引擎会给该函数创建一个函数执行上下文并将它push到当前执行栈的栈顶,当栈顶的函数执行完成后,栈顶的函数执行上下文会从执行栈中pop出,交由下一个执行上下文,so程序结束之前,执行栈最底部永远是globalContext
作用域链(scope chain):
它在js解释器进入到一个执行环境时初始化完成,并将其分配给当前执行环境。每个执行环境的作用域链由当前环境的VO和父级环境的作用域链构成
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f();
}
checkscope();
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
checkscope()();
上面这两个例子都输出“local scope”,两者的差别在于:执行栈的变化不一样!两者的流程如下:
demo1:
ECStack.push(<checkscope> functionContext);
ECStack.push(<f> functionContext);
ECStack.pop();
ECStack.pop();
demo2:
ECStack.push(<checkscope> functionContext);
ECStack.pop();
ECStack.push(<f> functionContext);
ECStack.pop();
具体的流程分析见:JavaScript深入之执行上下文
执行上下文的创建:
执行上下文分为两个阶段创建:1.创建阶段; 2.执行阶段
1.创建阶段
在JavaScript代码执行前,执行上下文处在创建阶段,在创建阶段会确定如下三个事情:
- 确定this的值(即This Binding)
- 创建词法环境(LexicalEnvironment)
- 创建变量环境(VariableEnvironment)
LexicalEnvironment和VariableEnvironment的区别:前者是存储function声明和let/const绑定,后者仅用于存储var绑定
对照概念理解:
let a = 20;
const b = 30;
var c; function multiply(e, f) {
var g = 20;
return e * f * g;
} c = multiply(20, 30);
GlobalExectionContext = { ThisBinding: <Global Object>, LexicalEnvironment: {
EnvironmentRecord: {
Type: "Object",
// 标识符绑定在这里
a: < uninitialized >,
b: < uninitialized >,
multiply: < func >
}
outer: <null>
}, VariableEnvironment: {
EnvironmentRecord: {
Type: "Object",
// 标识符绑定在这里
c: undefined,
}
outer: <null>
}
} FunctionExectionContext = { ThisBinding: <Global Object>, LexicalEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// 标识符绑定在这里
Arguments: {0: 20, 1: 30, length: 2},
},
outer: <GlobalLexicalEnvironment>
}, VariableEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// 标识符绑定在这里
g: undefined
},
outer: <GlobalLexicalEnvironment>
}
}
只有遇到multiply函数调用时才会创建该函数执行上下文!
注意:(在声明之前访问变量的区别)
let和cons定义的变量在创建阶段会保持未初始化状态,没有任何和它相关联的值,所以在声明之前访问let和const定义的变量会提示引用错误!
而var定义的变量会在声明的时候被置为undefined,所以在声明之前访问var定义的变量会输出undefined。
2.执行阶段
完成对所有变量的分配,最后执行代码
变量对象(VO)
每一个执行上下文都会有一个相关联的变量对象,变量对象的属性由在执行上下文中定义的变量(variables)和函数声明(function declaration)构成。
变量对象和当前作用域息息相关,不同作用域的变量对象互不相同!!
注意!!!函数声明会加到变量对象中,但是函数表达式则不会
// 函数声明
function a() {
...
} // 这个是函数表达式
var a = function funA(){ // a会作为变量存在VO中,但是funA不会存在VO中
...
}
在全局上下文中:当js编译器开始执行时会初始化一个Global Object,在浏览器端,Global Object == Windows对象 == 全局环境的VO。VO对于程序而言是不可读的,只有编译器才有权访问变量对象,因此Global Object对于程序而言是唯一可读的VO。
在函数上下文中:参数列表(parameters)也会被加入到变量对象中作为属性。用活动对象(AO)来表示变量对象,活动对象是在进入函数上下文的时刻被创建,这时候对象上的各种属性才能被访问。
活动对象(activation object)
调用函数时,会创建一个活动对象分配给执行上下文。AO由局部变量arguments初始化而成,所有作为参数传入的值都是该arguments数组的元素。随后,AO被当做VO用于变量初始化。
以我学习变量对象的例子为例对照记忆:
function foo(a) {
var b = 2;
function c() {}
var d = function() {}; b = 3;
} foo(1);
初始化时的AO是:
AO = {
arguments: {
0: 1,
length: 1
},
a: 1,
b: undefined,
c: reference to function c(){},
d: undefined
}
可以看到形参arguments是直接赋值的,而变量是置为undefined;代码执行后,变量赋值,修改变量的值,此时的AO如下:
AO = {
arguments: {
0: 1,
length: 1
},
a: 1,
b: 3,
c: reference to function c(){},
d: reference to FunctionExpression "d"
}
javascript执行上下文和变量对象的更多相关文章
- 透过一道面试题来探探JavaScript中执行上下文和变量对象的底
在做面试题之前,我们先搞清楚两个概念 执行上下文(execution context) 变量对象(variable object) 执行上下文 我们都知道JavaScript的作用域一共分三种 全局作 ...
- 再看javascript执行上下文、变量对象
突然看到一篇远在2010年的老文,作者以章节的形式向我们介绍了ECMA-262-3的部分内容,主要涉及到执行上下文.变量对象.作用域.this等语言细节.内容短小而精悍,文风直白而严谨,读完有酣畅淋漓 ...
- javascript 执行环境,变量对象,作用域链
前言 这几天在看<javascript高级程序设计>,看到执行环境和作用域链的时候,就有些模糊了.书中还是讲的不够具体. 通过上网查资料,特来总结,以备回顾和修正. 要讲的依次为: EC( ...
- JavaScript——执行环境、变量对象、作用域链
前言 这几天在看<javascript高级程序设计>,看到执行环境和作用域链的时候,就有些模糊了.书中还是讲的不够具体.通过上网查资料,特来总结,以备回顾和修正. 目录: EC(执行环境或 ...
- 【转】javascript 执行环境,变量对象,作用域链
这篇文章比较清晰的解释了一些作用域链相关的概念,忍不住收藏了 原文地址:http://segmentfault.com/a/1190000000533094 前言 这几天在看<javascrip ...
- JavaScript 执行环境 与 变量对象
什么是JS的执行环境? function funA(){ //一段代码静静的躺在这里,不能叫执行环境 } funA(); //当代码开始执行以后,系统会将它存入执行栈,并为他准备好足够的内存空间使用 ...
- JavaScript学习系列之执行上下文与变量对象篇
一个热爱技术的菜鸟...用点滴的积累铸就明日的达人 正文 在上一篇文章中讲解了JavaScript内存模型,其中有提到执行上下文与变量对象的概念.对于JavaScript开发者来说,理解执行上下文与变 ...
- 深入理解 JavaScript 执行上下文和执行栈
前言 如果你是一名 JavaScript 开发者,或者想要成为一名 JavaScript 开发者,那么你必须知道 JavaScript 程序内部的执行机制.执行上下文和执行栈是 JavaScript ...
- 转:JS高级学习笔记(8)- JavaScript执行上下文和执行栈
必看参考: 请移步:博客园 JavaScript的执行上下文 深入理解JavaScript执行上下文和执行栈 JavaScript 深入之执行上下文 写在开头 入坑前端已经 13 个月了,不能再称自己 ...
随机推荐
- window.onload=function(){};
window.onload=function(){}; 只要页面加载完毕,这个事件才会触发 扩展事件--页面关闭后才触发的事件 window.onunload=function(){}; 扩展事件-- ...
- css---6 选择器声明的优先级
选择器的特殊性由选择器本身的组件确定,特殊性值表述为4个部分,如 0,0,0,0一个选择器的具体特殊性如下确定: 1.对于选择器中给定的ID属性值,加 0,1,0,0 2.对于选择器中给定的各个类属性 ...
- 关于jquery ajax项目总结
$.ajax({ url:"../action/BorrowAction.php?action=oready", async:true,//异步请求 ...
- java哈希表(线性探测哈希表。链式哈希表)
哈希表(散列表) 通过哈希函数使元素的存储位置与它 的关键码之间能够建立一一映射的关系,在查找时可以很快找到该元素. 哈希表hash table(key,value) 的做法其实很简单,就是把Key通 ...
- csps模拟92数列,数对,最小距离题解
题面:https://www.cnblogs.com/Juve/articles/11767225.html 数列: 简化题意:已知a,b,c,求满足$a*x+b*y=c$的$x+y$最小值 然后ex ...
- 0922CSP-S模拟测试赛后总结
连发三篇爆炸实录我的心态竟然还这么好…… 昨天题目的D2.稍难. 这也不是我连续拿倒数第一的理由. T1不会.赛时硬写了一个30分的三次方暴力.还有一个地方写挂了.如果不是数据足够水我就爆零了. 也就 ...
- 安装Docker 服务
curl -fsSL https://get.docker.com/ | sh 执行到这一部分出错: The program 'curl' is currently not installed. Yo ...
- 求教各路大神,Fillder的证书一直无法在手机上打开,请教怎么解决
我跟足大神们的设置,软件是Fiddler4,手机是ios12.3.1. FD上该打勾的打勾了,该装证书的也装了,有帖子说重装证书和软件我也都试过,电脑也下了NET Framework 4.7_4.7. ...
- python基础语法(变量与数据类型)
python基础语法(变量与数据类型) 一.python变量 python中的变量不需要声明.每个变量在使用钱都需要赋值,变量赋值以后,该变量才会被创建 在python中,变量就是变量,它没有类型,我 ...
- 2019-8-30-WPF-一个性能比较好的-gif-解析库
title author date CreateTime categories WPF 一个性能比较好的 gif 解析库 lindexi 2019-08-30 08:59:45 +0800 2018- ...