执行上下文(也称执行环境)堆栈
执行上下文是javascript最重要的一个概念,执行上下文定义了变量或函数有权访问的其他数据,决定了它们各自的行为。而在javascript中有三种执行上下文: 全局执行上下文, 函数执行上下文, eval执行上下文。代码在其执行上下文中执行。在javascript中只有一个全局执行环境(根据宿主环境的不同,全局执行环境的对象也不一样)。可以有许多函数和eval执行环境的实例,每次调用一个函数或eval,都会进入对应执行环境执行代码。注意一个函数可能会产生无限上下文集合,因为每次函数调用自身都会产生一个新的执行上下文
执行上下文可以激活另一个执行上下文,例如函数调用另一个函数(或者全局执行上下文调用全局函数)等等,逻辑上就成了一个堆栈。这被称为执行上下文堆栈。
当执行流进入一个函数时,函数的上下文就会被推入一个栈中,如果在当前函数中调用另一个函数,当前函数就会暂停执行,并将执行流传递给被调用函数(被调用函数同事可能是其他函数的调用者),被调用者被推入堆栈。当被调用者的上下文结束后,将执行流交还给调用者,调用者的继续运行代码,直到结束后,栈将上下文弹出。
执行上下文
每个执行上下文可以抽象成一个对象,都包含了一组属性。
变量对象(variable object)
每个执行上下文都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象。但是不包含函数表达式和this(因为他不是一个变量)。
var foo = 10;
function bar(){};
(function baz(){})
console.log(baz); //eror
全局上下文的变量对象
如果执行上下文是一个函数,则将其活动对象作为变量对象,除了变量和函数声明之外,他还存储形式参数和arguments对象。
function foo(x, y) {
var z = 30;
function bar() {}
(function baz() {});
}
foo(10, 20);
我们发现baz
不在活动对象里。
作用域链(scope chain)
作用域链本质上就是根据名称查找变量(标识符名称)的一套规则。规则非常简单,在自己的变量对象里找不到变量,就上父级的变量对象查找,当抵达最外层的全局上下文中,无论找到还是没找到,查找过程都会停止。查找会在找到第一个匹配的变量时停止,被称为遮蔽效应
var x = 10;
(function foo(){
var y = 10;
(function bar(){
var z = 10;
console.log(x+y+z)![scope-chain.png][6]
})
})
如下图:
作用域知识点
词法作用域
词法作用域就是定义在词法阶段的作用域。换句话说,词法作用域是由你在写代码时将变量和块作用域写在哪里决定的,无论函数在哪里被调用,也无论他如何被调用,他的词法作用域只由函数被声明位置决定。
欺骗词法
在javascript中的eval
函数可以接受一个字符串为参数,并将其中的内容视为在书写时就存在于程序中这个位置的代码。
function foo(str, a){
eval(str); //欺骗
console.log(a, b);
}
var b = 2;
foo("var b = 3;", 1); //1, 3
函数作用域
函数作用域有两种方式
//函数声明
function foo(){
var a = 3;
console.log(a);
}
//函数表达式
(function foo(){
var a = 2;
console.log(a);
})
两者的区别在于它们的名称标识符会被绑定到何处,第一段代码中会被绑定到所在作用域中,第二段代码被绑定在函数表达式自身的函数中而不是所在作用域中。
块作用域
在javascript中没有块作用域,也就是说在{...}
中声明的变量会泄露到外面作用域
if(true){
var foo = 'dog'
}
console.log(foo); //dog
function dosomething(i){
console.log(i);
}
for(var i = 0; i < 10; i++){
dosomething(i);
}
console.log(i);
而ES6中新增的let可以将变量绑定到所在的任意作用域(通常是{...}内部),换句话说,let为其声明的变量隐式的劫持了所在的块作用域。
if(true){
var foo = 'dog'
}
console.log(foo); //dog
function dosomething(i){
console.log(i);
}
for(let i = 0; i < 10; i++){
dosomething(i);
}
console.log(i); //error
总结
作用域其实是有执行上下文中的变量对象和作用域链共同构成的。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。