详解javascript中的词法作用域

所谓作用域,就是一个声明起作用的那一段的程序代码。而作用域又分为词法作用域(也叫静态作用域)和动态作用域。静态作用域是指在编译的时候确定的,换句话说就是在声明时就确定了所绑定的作用域。而动态作用域是指运行时作用域,也就是说根据程序运行时候的信息流来动态确定的。

词法作用域(静态作用域)

通常,大部分语言都是静态作用域,Javascript也不例外,我们来看下面这几段代码

var foo='efg';
function a(){
console.log(foo) //会输出什么呢?
}
function b(){
var foo = 'abc'
a()
}
b();//efg
function a(){
console.log(foo) //会输出什么呢?
}
function b(){
var foo = 'abc'
a()
}
b();// foo is not defined.

为什么会出现上述的输出呢,这就是作用域的问题,每个函数都有自己的执行环境。静态作用域代表a()的作用域是在a声明的时候就确定了自己的作用域,也就是说a的作用域链,第一个对象是自己的arguments,第二个对象指向的是全局对象,所以在a()执行时查找foo的时候并不会进入到b()里面查找foo的值。

动态作用域

动态作用域不关心函数和作用域在哪声明和如何声明,只关心它们在何处调用。那么上述的代码执行结果将会是输出'abc'。很好理解,在a()执行时,需要访问到foo的时候,是在a的执行环境里面去查找foo,所以在b里面查找了foo的定义和声明。

javascript中的作用域

javascript简单明了,只有词法作用域,但是如eval()、with、this机制,使用上去很像是动态作用域,因此使用上要特别注意。

with语句和try-catch语句中的catch块会加长作用域链。在作用域前端添加一个变量对象。

作用域链

在B端,全局执行环境被认为是window对象,所有的全局变量以及函数都是作为window的属性和方法而创建的。当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链最前端,总是指向当前执行代码所在环境的变量对象。如果这个环境是一个函数作用域,则将其活动对象作为变量对象。作用域链的下一个变量对象是包含(外部)环境,直至到全局执行环境,全局执行环境的变量对象始终是作用域链中最后一个对象。搜索标志符时总是逐级往上,直至找到标志符为止。

window
    --foo
    --a()
    --b()
        --foo

Tips:ES6中引入了let方式声明变量,也就是说引入了块级作用域。在ES6以前,javascript不存在块级作用域,所以在例如for if 中声明的变量会被加入当前的执行环境。

Tips:关于ES6中箭头函数(也就是lambda表达式)中的this其实是静态作用域。

你可能感兴趣的:(详解javascript中的词法作用域)