作用域在函数定义时就已经确定了,执行上下文环境在函数调用时才确定。在全局作用域和函数作用域中会创建执行上下文环境(有闭包存在时,一个作用域存在两个上下文环境也是有的)。函数每调用一次都会产生一个新的执行上下文环境。但是处于活动状态的执行上下文环境只有一个,这是一个压栈出栈的过程。
执行上下文:函数每调用一次,都会产生一个新的执行上下文环境,因为不同的调用可能就有不同的参数。
let a = 10, fn, // 1、进入全局上下文环境 bar = function(x) { let b = 5 fn(x + b) // 3、进入fn函数上下文环境 } fn = function(y) { let c = 5 console.log(y + c) } bar(10) // 2、进入bar函数上下文环境
首先(数字1注释),执行代码之前,首先创建全局上下文环境(活动状态)如下:
// 全局上下文环境 a: undefined fn: undefined bar: undefined this: window
之后开始执行代码,代码到10行之前,上下文环境中的变量都在执行过程中被赋值如下:
// 全局上下文环境 a: 10 fn: function bar: function this: window
然后执行到bar(10) ,跳转到bar函数内部,执行函数体语句之前,在bar函数内会创建一个新的执行上下文环境如下,并且将bar执行上下文环境压栈。
// bar执行上下文环境 b: undefined x: 10 arguments: [10] this: window
然后执行到数字3注释那里,调用fn函数,执行函数体语句之前,会创建一个新的执行上下文环境如下,并且将fn执行上下文环境压栈。
// fn执行上下文环境 c: undefined y: 15 arguments: [15] this: window
在上边步骤中,fn执行完毕后,调用fn函数生成的fn上下文环境出栈,被销毁。然后bar执行完毕后,调用bar函数生成的上下文环境出栈,被销毁。然后剩下全局上下文环境,出栈销毁。具体步骤如下图:
作用域:JavaScript 采用的是词法作用域,即函数的作用域在函数定义的时候就决定了,在外部作用域无法访问内部作用域中的变量。
作用域链:如果要取得一个变量得值,首先会在当前的作用域中寻找,如果没有,就会到外层作用域中寻找,再没有在向外层的作用域中找,找到了就结束了,找不到就报异常了,就形成了作用域链。
每个函数作为一个作用域,如果出现函数嵌套函数,则就会出现作用域链。JavaScript的作用域在被执行之前已经创建,之后后再去执行时只需要按照作用域链去寻找即可。
自由变量:比如变量a,是在fn函数作用域使用,但是并没有在fn作用域声明,而是在别的作用域声明,这就是自由变量。