1.词法作用域
词法作用域又被称为静态作用域。函数的词法作用域是由函数的声明位置决定的。函数一旦声明,他的词法作用域就确定了。一个函数的词法作用域包括这个函数的形参、函数内部声明的变量以及函数内部声明的函数。我们可以将“词法作用域”中的“词法”理解为声明的标识符。
//在全局下 找到了词 foo
function foo(a) {
//foo下 找到了词 a , b , bar
var b = a * 2;
function bar(c) {
//在bar下 找到了词 c
console.log(a, b, c);
}
bar(b * 3);
}
foo();
相应地,也有动态作用域的概念,指的是函数的作用域由函数的调用位置决定。动态作用域在JS中不存在。
2.作用域
- 作用域是一套规则,用来确定在何处以及如何查找标识符。
- 作用域变量的查找机制:在当前作用域下无法查找到某个变量时 ,引擎就会在外层嵌套的作用域下继续查找, 直到找到该变量 或者抵达最外层的全局作用域为止。
- 作用域链用来进行标识符的查询,保证对变量和函数的有序访问的。
function foo(a) {
var b = a * 2;
function bar(c) {
console.log(a, b, c);
}
bar(b * 3);
}
foo();
在上面这段代码中,bar()
在执行时,会去查找a
、b
、c
这三个变量,由于自身的作用域内,只存在了形参c
,没有声明a
与b
变量,根据作用域变量的查找机制,会去外层的foo()
中查找a
与b
,在foo()
中查找到了形参a
与声明的变量b
,与是停止查找开始赋值执行代码。在查找时,是根据作用域链来决定查找顺序的。bar()
的作用域链: bar
=>foo
=>全局作用域(window
)。foo()
的作用域链:foo()
=>全局作用域(window
)。
3.函数的执行环境
执行环境也叫执行上下文,每一个执行环境都有一个与之关联的变量对象,在这个环境中定义的所有的函数和变量都会保存在这个对象上,包括arguments
和this
。
函数的执行环境实在函数执行时确定的,函数执行时,所需的变量都是从函数的执行环境中取出的,因此执行环境中变量的值,是会随着函数执行而发生变化的。
函数的执行环境在函数执行时会经历三个阶段,创建阶段、执行阶段、销毁。所以每次执行函数,执行环境都是一个新的执行环境。下面的函数在执行时:
var a = 2;
function fn(x) {
//arguments
//this
var b = 2;
return a + b + x;
}
fn(0);
首先会创建执行环境,在创建阶段有三个主要活动:
1).生成变量对象
2).建立作用域链
3).确定 this 指向
此时的变量对象:
fn(0)的执行环境的变量对象:
{
x:0,
a:2,//来自全局执行环境
b:undefined,//此时还未执行 尚未赋值
arguments:[],//还未执行 尚未传入参数
this:window
}
创建完成之后,进入执行阶段,执行阶段的执行环境变量对象被称为活动对象。在执行阶段,主要活动有:
1).变量赋值
2).函数引用
3).执行其他代码
此时,执行环境活动对象为:
fn(0)的执行环境的变量对象:
{
x: 0,
a: 2, //来自全局执行环境
b: 2, //赋值
arguments: [0], //传入一个参数 0
this: window,
};
在函数执行完成之后,执行环境会从执行环境栈中被移出销毁。下次执行环境时,会重新创建一个执行环境。
4.执行环境栈
执行环境栈其实就是一个执行环境压栈和出栈的过程。
在浏览器第一次加载JS时,就默认进入了全局执行环境,此时便会将全局执行环境压入栈中。当退出浏览器时,全局执行环境也会被销毁。
var a = 1;
var b = 2;
function fn(x) {
//arguments
//this
var a = 10;
function bar(x) {
var a = 100;
b = x + a;
}
bar(20);
bar(200);
return b;
}
fn(0);
上面这段代码,
加载完毕之后,全局环境被压入执行环境栈,此时全局执行环境为活跃状态,
在执行fn(0)
时,会将fn(0)
的执行环境压入执行环境栈,此时栈中有全局执行环境和fn(0)
的执行环境并且fn(0)
的执行环境进入了活跃状态,
在fn(0)
的执行至bar(20)
时,会将bar(20)
的执行环境压入执行环境栈,此时bar(20)
的执行环境成为了活跃状态,bar(20)
执行完成,会将bar(20)
的执行环境从栈中取出销毁,此时活跃状态回归到了fn(0)
的执行环境
随后执行bar(200)
;重复了一遍执行bar(20)
的过程。
随后fn(0)
执行完毕,fn(0)
的执行环境被从栈中取出销毁,活跃状态回归到了全局执行环境。
函数作用域与执行环境的不同
- 作用域在函数声明时就已经确定了,所以作用域是静态的。
- 每个函数都会创建自己的作用域,通过作用域可以查找到当前作用域范围内的变量和函数有哪些,但是不知道变量的值。
- 执行环境是在函数执行时确定的,包含了作用域内的所有变量和函数的值,执行环境是动态的。
- 每次函数执行时的执行环境都是不同的,每次执行都是创建一个新的执行环境,形成新的执行环境栈。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。