前端常见知识点(三)
作用域
前面讲到闭包的时候,我们说闭包是「是指有权访问另一个函数作用域中的变量的函数」,那么所谓的「作用域」又是什么呢?
什么是作用域?
先来看一个例子:
1 | |
在这段代码中,我们在greet函数外部定义了一个变量a,函数greet首先在自己的作用域中查找变量a,没有找到后再在外部的全局作用域中查找,找到之后将其打印输出。
接下来是另外一个例子:
1 | |
在这段代码中,我们在greet函数内部定义了一个变量b,调用greet函数能够正常将其打印输出,但是当我们在外部直接输出变量b的时候却报错了,也就是说,在全局作用域里,变量b是未定义的。
于是我们知道,JavaScript 的作用域是通过函数来实现的,在函数内部定义的变量,在函数的外部是不可以访问的。
在 ES6 的let、const关键字提出来之前,JavaScript 的作用域只有两种:「全局作用域」和「函数(局部)作用域」。JavaScript 是没有「块作用域」的。
作用域链
依旧是第一个例子:
1 | |
我们查找变量a的过程是:首先在函数greet的作用域中查找,之后到函数的上一层(这里是全局)作用域中查找,这样逐步向外查找的过程形成一个链条,就称作「作用域链」。
下面来看一个例子:
1 | |
如果把inner函数中的a的定义注释掉,那么ineer()的输出就是outter;如果再把outter函数中的a定义注释掉,那么三个输出就全都是window;这里的作用域链就是inner->outter->window(全局)。
延长作用域链
尽管在 JavaScript 中,作用域只有全局和局部(函数)两种,但是作用域链可能会在某些情况下得到加长。
try-catch语句的catch块;with语句。
在「红宝书」中有一个 with语句的例子:
1 | |
在这个例子中,with语句接受到的是location对象,因此其变量对象中就包含了location对象的所有属性和方法,而这个变量对象被临时添加到了作用域链的前端,作用域链就变成了:location对象->buildUrl的变量对象->windows全局对象,于是当在with语句中引用变量href时,实际引用的就是location.href。
至于try-catch语句:
1 | |
对于catch语句来说,会创建一个包含着被抛出的错误对象的声明的新的变量对象,并将其置于作用域链的头部,当catch语句块的代码执行完,会移除该变量对象,销毁作用域。