举个例子,汽车,绝对是个非常有价值的stuff,它给我们的日常出行,货物运输等带来了极大的便利;筷子,同样也是个非常有价值的stuff,它给我们吃饭带来了极大的方便。但是,汽车能帮我们把菜送到嘴里吗?筷子能载着我们出行吗?
那么,我上面所说的某些领域,我们是不是可以称其为作用域,我想是可以的。
说到这,那么我就想问了:在JS里,作用域是不是也是类似的概念呢?
首先,我可以肯定的说这是一个在JavaScript中灰常灰常重要的概念,关系着JS里很多核心的机制,理解它,很多问题都迎刃而解了。
那么,问问自己,在JS里,作用域是什么?
心里大概知道是什么,但是细细一想又好像说不太清。
没关系,下面我们就细细品味这个有意思的东东。
先throw概念吧:
作用域负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。
通俗来说,作用域相当于一个管理员(有自己的一套规则),他负责管理所有声明的标识符的有序查询。
我们来讲个故事,说说作用域到底干了啥。
三兄弟齐上阵
long long ago,有3个关系很好的基友,老大叫引擎,老二叫编辑器,老三叫作用域。三兄弟眼看年岁已长,可手上还是没有几个银子。个个都很着急,于是三兄弟谋划一同做个事。
求职过程:此粗略去数万个字。。。
最终他们做的工作是:负责JS的编译和运行。
他们的工作内容是这样的:
var a = 1;
console.log( a );
开始工作:
- 编译器:作用域,帮我看看你那有没有储存变量a。
作用域:二哥,还没有。
编译器:那好,帮我储存一个。
引擎: 老三,你那有没有一个叫做a的变量。
编译器:大哥,还真有,刚二哥让我存储了一个。
引擎: 真是太好了,帮我拿出来,它的值是几,我需要给它复制。
编译器:大哥,它的值是2。
引擎: 谢谢你,三弟,这样我就能打印它的值了。
上面讲了一个不恰当的小故事,但是三者之间的关系大概就是这样。
彻底搞懂JavaScript作用域里介绍过,大部分标准语言编译器的第一个工作阶段叫作词法化(也叫单词化)。回忆一下,词法化的过程会对源代码中的字符进行检查,如果是有状态的解析过程,还会赋予单词语义。
在JS里,使用的作用域就是词法作用域。
简单地说,词法作用域就是定义在词法阶段的作用域。换句话说,词法作用域是由你在写代码时将变量和块作用域写在哪里来决定的,因此当词法分析器处理代码时会保持作用域不变(大部分情况下是这样的)。
var a = 1;
function fn() {
var a = 2;
console.log( this.a );
}
fn(); // 1
从上面的代码,我们可以看出:fn中打印a的值不是由写代码的位置确定的,而是取决于fn执行的位置。
区别
词法作用域是在写代码或者说定义时确定的,而动态作用域是在运行时确定的。(this 也是!)
词法作用域关注函数在何处声明,而动态作用域关注函数从何处调用。
函数作用域
JS里,生成作用域的方式:
函数
with、eval (不建议使用,影响性能)
由此,我们知道JS里,绝大多数的作用域都是基于函数生成的。
每个函数都会为自身生成一个作用域气泡。这个气泡内所有的标识符都可以在这个气泡中使用。
function bar() {
var a = 1;
function fn() {
var b = 2;
console.log(b);
}
fn();
console.log(a);
}
bar();
上面代码,bar气泡有标识符a、fn,因此在bar气泡中可以访问到a、fn; fn气泡有标识符b,因此在bar气泡中可以访问到b; 当然还有一个全局气泡,全局气泡中有bar标识符,因此在全局气泡中可以访问到bar。