执行上下文这个词大家都听过,但是里面内容有啥呢,那就继续往下看看。
要明白上下文是啥,那么我们先来了解一下js的执行环境,运行过程等。
执行环境
在函数的那一张中,提到了全局环境和局部环境的概念,就是说在函数的执行过程中,函数内部的环境和外面是独立的,只是说函数内部可以使用外面全局环境的变量或者在调用全局函数的方法。在此,来总结一下js的执行环境。
- Global全局环境:这个全局的环境,是js引擎一启动的时候就加载好的环境,用于处理任何JS代码(变量的定义,函数声明,函数调用等);
- Function函数体环境:对于function函数环境来说,大部分人都清楚。函数内部是一块独立的空间,变量定义,再次声明函数,函数调用等都可以做到(当函数调用时候才会执行)。函数的本质是:降低代码的耦合详情查看
- eval环境:使用eval()函数动态执行的JS代码
不同类型的JS代码具有不同的执行环境,所有的JS代码都是在一个执行环境中被执行的。执行环境是一种概念,同时也是一种机制。用来描述js运行过程(处理每一行代码所做的事情)。执行环境定义了变量或函数是否具有访问其他数据的权限,进而决定各自行为。
运行过程
这里讨论js的代码的运行过程,但是在讨论前,这里先介绍几个特殊的名词:
- call stack(执行栈): 栈的特点是FILO(first in, last out).js在执行代码的时候,会往执行栈中开辟一个内存空间,当代码执行完成后,相应的内存空间释放。
- VO(variable object)变量对象: 就是说js在执行栈中创建的内存空间是一个代码执行环境,每一个执行环境都会有一个变量映射表(类似对象),来记录当前环境每一个变量对应的值。
- GO(global object)全局对象: 在执行栈中的最底层(一开进入栈的js执行环境)的vo对象,我们称之为全局对象,浏览器环境指代的是window对象,node环境指代的是global对象等
- AO(activity object)活动对象: 在执行栈中当前活跃的执行环境,就是说在每一次栈中最顶层的vo,我们称之为AO(活跃对象);
有了上面的那些概念,咋们来看一段代码,然后探讨一下代码是怎么运行的。
var global = 0; console.log(global); // 0
代码运行图如下:
函数执行环境的规则
虽然说js是解释性语言,但是在js在运行代码的时候会先扫码一下代码,然后遇到函数环境会做以下事情:
1.确定形参和argument的参数一一对应;
2.确定本函数体内(除了内部的其他声明函数的区域)var 声明的变量(var 里面可以保存,函数,对象等),将他的值设置为undefined,如果当前的VO中已有变量则忽略。也就是说,argument在VO中的变量会是第一个声明,并且值是undefined;
3.确定通过字面量声明的函数(function aaa(){}的形式),将他的值设置为函数的地址,如果VO中存在,会被覆盖
当一个上下文中执行的代码的时候,如果当前VO不存在某个属性,将会通过作用域链的上一个VO中寻找。这里的作用域链是一块比较大的内容,留给后面的文章中。
上面说到了函数执行环境的具体规则。那么咋们来小试牛刀,拿几道题目来练练手。不然原理说那么多也容易忘。
console.log(a); var a = 123; function a() { console.log('眼见为实'); }; console.log(a); function a() { console.log('理解才是真'); }; console.log(a)
代码的开始的扫码图如下:
通过代码的扫码预检,我们可以发现,全局的GO对象中的a,最后被赋值给了fn(fn是一个函数,并且地址指向函数的内容是理解才是真),所以第一行输出 fn(理解才是真),然后执行代码,a 被赋值给了123,后面的函数被预检后就没有任何作用。所以后面两个输出的结果都是 123;
结果
为了方便大家理解执行上下文,所以再来一道题目。
题目升级
var g1 = 123; function Test(a,b){ console.log(a,b,g1); var b = 123; function b(){}; var a = function(){}; console.log(a,b) } var g2 = 456; var g3 = function(){}; Test(1,2);
这一道题目就不画执行上下文的内存图了,有兴趣的可以去画一画,加深自己的理解。
解题:
// 第一步扫描代码,看看GO中存在哪些var声明的变量,先设置成undefined GO:{ g1: undefined g2: undefined g3: undefied } // 第二步扫描代码,看看GO中存在哪些有没有函数声明 GO:{ g1: undefined g2: undefined g3: undefied Test: fn } // 第三步: 执行代码,赋值GO GO:{ g1: 123 g2: 456 g3: fn(g3) Test: fn(test) } // 第四步执行Test函数,查看形参与实参对象,并且设置Test中VO的变量 VO(Test){ a:1, b: 2 } // 第五步执行Test函数,查看Test中VO使用var声明的变量 在这一步中会发现使用var定义的a,b在形参中都存在了,不管 // 第六步执行Test函数,查看Test中VO使用函数声明的变量 VO(Test){ a:1, b: fn() } // 第六步执行Test函数,逐行执行代码 第一行console.log 输出的a和b在当前的VO中都存在,g1不存在去全局的GO中寻找,得出的结果是 1,fn(), 123 第二行b是一个赋值语句,赋值给了123 VO(Test){ a:1, b: 123 } 第三行b那个声明函数不用管 第四行的a赋值给了一个函数 VO(Test){ a:fn(), b: 123 } 第五行输出结果的a和b在当前的VO中都存在,结果是 fn, 123
结果:
能看到这里的不容易,我为你点赞,但是对于这种理论性的知识,自己一定需要多画一画,有练一练才可能掌握哦!