聊起作用域,很多小伙伴并不陌生,是所有编程语言最基本的功能之一,就是在某个范围内储存变量的值。
也可以说,“作用域就是根据名称查找变量的一套规则”。
想入深入学习作用域,必先理解作用域。
理解后面一行代码在底层的工作原理: var a = 1 ;
这条语句在编译器会并不是一步处理掉的,它会被拆分成两条语句,var a ,a = 1 。
var a ,编译器会先查询询问变量a是否存在,再做声明操作。
a = 1,编译器也是一样,查询询问变量a是否存在,再做赋值存在。
由此看见,机器毕竟还是机器,每当碰到变量必先进行查询,询问作用域里面是否存在变量。
那么,如果作用域中找不到变量怎么办?一直往上层的作用域寻找,直到最外层的全局作用域,不管有没有找到,查找过程都会停止。
作用域按照工作模型来划分,作用域分两种,一种是词法作用域,一个动态作用域。
词法作用域:在代码的位置决定作用域,词法分析器处理代码的时候会保持作用域不变。
动态作用域:在运行的位置决定作用域。
换一句话来说,就是词法作用域关注函数在哪里声明,动态作用域关注函数从哪里调用。Javascript是属于词法作用域。
作用域按照容器区域来划分,作用域可以分为函数作用域和块作用域。
函数作用域
在Javascript中,每一个函数都有自己的函数作用域。属于这个函数的全部变量都可以在整个函数的范围内使用及复用。
但是在某些场合,我们必须要隐藏作用域(很多原因促成这个隐藏方法,比如避免冲突,变量污染等。)
请阅读以下代码:
var a = 1 ;
function demo(){
var a = 2;
console.log(a);//2
}
demo();
console.log(a); //1
这代码里面展示了函数作用域,可以在函数作用域里面做一些只能在作用域才能进行的操作。但是这个代码并不理想,这么运行会附带一些额外的问题,首先它声明了一个demo函数,污染了所在的作用域;其次需要再次运行demo()才能运行函数内代码。
为了解决上述问题。请阅读下面代码
var a = 1 ;
(function demo(){
var a = 2;
console.log(a);//2
})();
console.log(a); //1
这代码的函数前面加了 “(” 号,函数将以函数表达式的声明方式来声明。
这个做法解决了之前的弊端,避免了声明函数名来污染作用域,而且能给函数内部代码一个独立的运行作用域。
其实这个方法有另外一种名字,就是IIFE(立即执行函数表达式),这个函数名对于IIFE并不是必须的。
IIFE的作用非常广,下面是它的主要三个推广实践的优势。
- 创建只使用一次的函数,并立即执行它。
- 创建闭包,并保持状态,隔离作用域。
- 作为独立模块存在(如JQuery),防止命名冲突,命名空间注入,模块解耦。
块作用域
这个概念很可惜,在ES5之前,Javascript要实现块作用域是非常痛苦的事情。
在ES6之后,可以通过 let 关键字变量声明隐隐式地劫持所在的块作用域,达到块作用域的效果。
但是let进行的声明不会在块作用域内进行声明提升,只有 var 和 函数声明 才会进行声明提升。