你不知道的JavaScript之作用域

210 阅读4分钟

前言

在几乎所有编程语言中,都会有一个功能,就是能储存变量当中的值,并且之后能对这个值进行访问等一系列操作。那你有没有想过,变量是如何储存的呢?它会储存在哪里?最重要的是,程序在需要时要如何找到他们呢?在JavaScript语言中有一套设计好的规则用来储存这些变量,并能在之后方便的查找到这些变量,这套规则则被称作为作用域。

var a

var a = 1;这是所有人都会并且理解的一个语句,就是将1赋值给变量a。但是你有没有想过其实这一条语句可以拆分成两个部分:var aa = 1为什么要拆成两部分呢?在JavaScript语言中,我们可以把程序分成两个阶段,一个是编译阶段,一个是执行阶段,在代码执行前JavaScript中的编译器就先会对这段代码进行编译,也就是我们的前半段句话var a,编译器首先会询问当前作用域中是否已经存在一个变量a了,如果有,那么它就会忽略该申明,如果没有,那么他就会要求在当前作用域下申明一个新的变量a。

a = 1

在执行阶段,编译器首先会为引擎生成运行时所需的代码用来处理a = 1这个操作,之后引擎就会询问当前作用域下是否有a这个变量,如果有的话那么就会将1赋值给他,如果没有它就会继续向当前作用域的父级作用域继续进行查找,直到找到全局作用域为止,也就是沿着作用域链从下往上进行查找,如果还是没有找到则会报错并返回ReferenceError。

LHS和RHS

在引擎通过作用域查找变量的过程中,可以细分为两种查找方式:LHSRHS。通俗的来讲,LHS的意思就是为当前的值寻找一个容器,并将这个值装进去,如:a = 1RHS的意思就是需要通过查找拿到这个变量容器中存在的值,如console.log(a)

下面我们通过一个小小例子来更深刻的理解上面的两种查找方式:

function win(a) {
    var b = a;
    return a + b;
}
var c = win(1);

在上面这个小小例子中,我们有3个LHS和4个RHS

  • LHS:
  1. c = win(1) 这时我们需要将win(1)的值塞进c中
  2. a = 1 在访问win这个函数时,我们需要将win(1)中的1塞进a这个形参中
  3. b = a 我们将a的值塞进b中
  • RHS:
  1. 在c = win(1)中查找win(1)本身的值
  2. 在 b = a 中查找a的值
  3. 在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,

image.png

我们再来看接下来这段代码:

function foo(){
 b = 1;
}

foo();

console.log(b)

我们在控制台输出这段代码时,他会返回给我们一个1,也就是变量b的值,那为什么我们没有申明他也为什么没有报错而是返回了值呢,原因是引擎在运行foo这个函数中的b = 1这段代码时,在作用域中进行LHS查找,如果在所有作用域中没有找到这个变量,那么全局作用域中就会创建一个具有该名称的变量,并将其返回给引擎,前提是程序运行在非“严格模式”下。

image.png

小结

此篇文章概述了我对作用域的理解,我认为这是一个非常有意思的内容,从JavaScript的底层去深挖原理,会让你产生极大的好奇心,对于掌握这门语言来说是非常好的帮助,如果这篇文章对你有帮助的话,请不要吝啬你的三连~