前言
在几乎所有编程语言中,都会有一个功能,就是能储存变量当中的值,并且之后能对这个值进行访问等一系列操作。那你有没有想过,变量是如何储存的呢?它会储存在哪里?最重要的是,程序在需要时要如何找到他们呢?在JavaScript语言中有一套设计好的规则用来储存这些变量,并能在之后方便的查找到这些变量,这套规则则被称作为作用域。
var a
var a = 1;
这是所有人都会并且理解的一个语句,就是将1赋值给变量a。但是你有没有想过其实这一条语句可以拆分成两个部分:var a
和a = 1
为什么要拆成两部分呢?在JavaScript语言中,我们可以把程序分成两个阶段,一个是编译阶段,一个是执行阶段,在代码执行前JavaScript中的编译器就先会对这段代码进行编译,也就是我们的前半段句话var a
,编译器首先会询问当前作用域中是否已经存在一个变量a了,如果有,那么它就会忽略该申明,如果没有,那么他就会要求在当前作用域下申明一个新的变量a。
a = 1
在执行阶段,编译器首先会为引擎生成运行时所需的代码用来处理a = 1
这个操作,之后引擎就会询问当前作用域下是否有a这个变量,如果有的话那么就会将1赋值给他,如果没有它就会继续向当前作用域的父级作用域继续进行查找,直到找到全局作用域为止,也就是沿着作用域链从下往上进行查找,如果还是没有找到则会报错并返回ReferenceError。
LHS和RHS
在引擎通过作用域查找变量的过程中,可以细分为两种查找方式:LHS
和RHS
。通俗的来讲,LHS
的意思就是为当前的值寻找一个容器,并将这个值装进去,如:a = 1
。RHS
的意思就是需要通过查找拿到这个变量容器中存在的值,如console.log(a)
。
下面我们通过一个小小例子来更深刻的理解上面的两种查找方式:
function win(a) {
var b = a;
return a + b;
}
var c = win(1);
在上面这个小小例子中,我们有3个LHS和4个RHS
- LHS:
- c = win(1) 这时我们需要将win(1)的值塞进c中
- a = 1 在访问win这个函数时,我们需要将win(1)中的1塞进a这个形参中
- b = a 我们将a的值塞进b中
- RHS:
- 在c = win(1)中查找win(1)本身的值
- 在 b = a 中查找a的值
- 在return a + b中分别找到a的值和b的值
异常
区分LHS和RHS有什么用呢?我们来看下面这一段代码:
function foo(a){
console.log(a+b);
b = a
}
foo(1);
我们在控制台输出这段代码时候,他会报错并返回ReferenceError,这是因为引擎在执行RHS时发现并未在所有作用域中找到b这个变量,我们并没有对此变量进行申明少了var
。如果加上var的话b的值将会是undefined,
我们再来看接下来这段代码:
function foo(){
b = 1;
}
foo();
console.log(b)
我们在控制台输出这段代码时,他会返回给我们一个1,也就是变量b的值,那为什么我们没有申明他也为什么没有报错而是返回了值呢,原因是引擎在运行foo这个函数中的b = 1这段代码时,在作用域中进行LHS查找,如果在所有作用域中没有找到这个变量,那么全局作用域中就会创建一个具有该名称的变量,并将其返回给引擎
,前提是程序运行在非“严格模式”下。
小结
此篇文章概述了我对作用域的理解,我认为这是一个非常有意思的内容,从JavaScript的底层去深挖原理,会让你产生极大的好奇心,对于掌握这门语言来说是非常好的帮助,如果这篇文章对你有帮助的话,请不要吝啬你的三连~