什么是“执行上下文” (也叫做执行上下文环境) 暂且不下定义,先看一段代码

console.log(a);//=>Uncaught ReferencecError:  a is not defined
console.log(a);
var a; //undefined
console.log(a);
var a=10; //=>undefined

第一句报错,a未定义,很正常

第二句,第三局都是输出undefined,说明浏览器在执行console.log(a)时,已经知道了a是undefined,但却不知道a=10

在一段js代码拿过来真正一句一句运行之前,浏览器已经做了一些"准备工作" 其中就包括对变量的生命,而不是赋值,变量赋值在执行语句的时候进行,

1.png

这是第一种情况

下面还有。来个简单的

2.png

有js开发经验的朋友都知道,无论在哪个位置获取this,都是有值的,至于this取之情况,比较复杂

与第一种情况不同的是,第一种情况只是对变量声明(并没有赋值),而此种情况直接给this赋值。这也是“准备工作”情况要做出的事情之一

在第三种情况中,需要注意代码注释中的两个名词--“函数表达式”和“函数声明”虽然两折都很常用,但是两者在准备工作时,确实两种待遇

console.log(f1);//function f1(){}
function f1(){}//声明函数
console.log(f2);//undefined
var f2=function(){};//函数表达式

看以上代码,“函数声明”时我们看到第二种情况的影子,而“函数表达式”时我们看到了第一种情况的影子

没错在准备工作中,对待函数表达式就像对待 var a=10这样的变量一样,只是声明,而对待函数声明时,却把函数整个赋值了

总结一下,“准备工作”完成了哪些工作

  • 变量、函数表达式声明——变量申明,默认赋值为undefined;
  • this-赋值;
  • 函数声明 ——赋值

以上三种情况我们称之为“执行上下文”或者“执行上下文环境”

这里解释一下为什么代码段分为这三种。

所谓“代码段”就是一段文本形式的代码。

首先,全局代码是一种,这个应该没有非议,本来就是手写文本到 <script> 标签里面的。

<script type="text/javascript">

//代码段
</script>

其次,eval代码接收的也是一段文本形式的代码。

eval("alert(123");

最后,函数体是代码段是因为函数在创建时,本质上是 new Function(…) 得来的,其中需要传入一个文本形式的参数作为函数体。

3.png

这样解释应该能理解了。

如果在函数中,除了以上数据之外,还会有其他数据。先看以下代码

function fn(x){
    console.log(arguments);//[10]
    console.log(x);        //10
}
fn(10);

以上代码展示了函数体的语句执行之前,arguments变量和函数的参数都已经被赋值。从这里可以看出,函数每调用一次,都会产生一个新的执行上下文环境 因为不同的调用就会有不同的参数

另外一点在于 函数在定义的时候(不是调用的时候)就已经确定了函数内部自由变量的作用域

ex

var a=10;
function fn(){
    console.log(a);//a是自由变量。函数创建时,a就确定了要取值的作用域
}

function bar (f){
    var a=20;
    f();//打印“10”,而不是“20”
}
bar(fn);

总结了函数的附加内容,总结一下上下文环境的数据内容
全局代码的上下文环境数据内容为

普通变量(包括函数表达式)如 var a=10声明(默认赋值为undefined

|函数声明,如: function fn() { }|赋值|
|this|赋值|

如果代码段是函数体,那么在此基础上需要附加:

参数赋值
argument赋值
自由变量的取值作用域赋值

给执行上下文环境下一个通俗的定义 ———— 在执行之前,把将要用到的所有的变量都先拿出来,有的直接复制了,有的先用undefined占个空