JS作用域

483 阅读4分钟

执行上下文(也称执行环境)堆栈

执行上下文定义了变量或函数有权访问得其他数据,决定了它们各自得行为。而在JavaScript中有三种执行上下文: 全局执行上下文,函数执行上下文,eval执行上下文。 代码在其执行上下文中执行。在JavaScript中只有一个全局执行环境(根据宿主环境的不同,全局执行环境的对象也不一样)。可以有许多函数和eval执行环境的实例,每次调用一个函数或eval,都会进入对应执行环境代码。注意 一个函数可能会产生无限上下文集合,因为每次函数调用自身都会产生一个新的执行上下文

89241819-570d14fe33067.png

执行上下文可以激活另一个执行上下文,例如函数调用另一个函数(或者全局执行上下文调用全局函数)等等,逻辑上就成了一个堆栈。这被成为执行上下文堆栈。

当执行流进入一个函数时,函数的上下文就会被推入一个栈中,如果在当前函数中调用另一个函数,当前函数就会被暂停执行,并将执行流传递给被调用函数(被调用函数同时可能是其他函数的调用者),被调用者函数推入堆栈。当被调用者的上下文结束后,将执行流交还给调用者,调用者的继续运行代码,直到结束,栈将上下文弹出。

620422871-570d14feaf681_fix732.png

作用域链(scope chain)

作用域链本质上就是根据名称查找变量(标识符名称)的一套规则。规则很简单,在自己的变量对象里找不到变量,就上父级变量对象查找,当抵达最外层全局上下文中,无论找到还是没找到,查找过程都会停止。 查找会在找到第一个匹配的变量时停止,被称为遮蔽效应

var x = 10;
(function foo(){
    var y = 10;
    (function bar(){
        var z = 10;
        console.log(x+y+z)![scope-chain.png][6]
    })
})

1712643983-570d14ffae847.png

词法作用域

词法作用域就是定义在词法阶段的作用域。换句话说,词法作用域是由你在写代码时将变量和块作用域写在哪里决定的, 无论函数在哪里被调用,无论它如何被调用,它的词法作用域只由函数被声明位置决定。

欺骗词法

在JavaScript中的eval函数可以接受一个字符串为参数,并将其中的内容视为在书写时就存在于程序中这个位置的代码。

function foo(str, a){
    eval(str); //欺骗
    console.log(a, b);
}
var b = 2;
foo("var b = 3;", 1); //1, 3

全局作用域

全局作用域在页面打开时被创建,页面关闭时被销毁;在全局作用域中有全局对象window,代表一个浏览器窗口,由浏览器创建,可以直接调用;全局作用域中声明的变量和函数会作为window对象的属性和方法保存.

var a = 10;
b = 20;
function an(){
    console.log('an')
}
var bn = function(){
    console.log('bn')
}
console.log(window)

函数作用域

函数作用域有两种方式

//函数声明
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

总结

作用域其实是有执行上下文中的变量对象和作用域链共同构成的。