读《你不知道的JS》的笔记,有问题请指出。
html
每一个编程语言的一个最基本的功能,就是能够声明变量,在变量里储存值,更改值,访问值。
随之一系列问题产生,这些变量存储在哪里,未来须要使用他们的时候如何获取他们?
这表明咱们须要有一套设计良好的规则知道如何存储这些变量,并如何获取到他们,而这套规则,被称为做用域(Scope)。编程
不只是在执行时会用到做用域,编译时也会用到做用域编程语言
咱们用 JS 写的代码称为源代码,是一种人类能看懂的语言,因为计算机只能读懂 0 和 1(二进制/机器语言),因此若是咱们要在计算机上执行咱们的代码,在执行代码以前,有个编译过程,目的是将源码编译成机器能够理解的机器码。函数
通常来讲,编译分为 3 个步骤设计
var a = 2
这行代码,当词法单元生成器(tokenizer)在判断 var
是单独的一个词法单元仍是属于其余词法单元的一部分时,若是调用的是有状态(stateful)的解析规则,那么字符串被转换成词法单元的这个过程就被称为词法分析,不然,就是分词。var a = 2
这一系列任务。编译完以后,就是执行代码了,对于 JS 来讲,即便是 var a = 2
这行很简单的代码,在通过编译和执行这两个步骤时,也会涉及到做用域。code
JS 的编译过程和其余语言的编译过程有点不一样,在编译的第三步,也就是将抽象语法树(AST)转换成机器指令时,当编译器碰到声明操做时,如 var a = 2
,编译器会询问做用域,在当前的做用域内,是否声明过 a,若是没有,就会让做用域在当前做用域内声明一个 a,不然,忽略该声明。htm
从上面能够看出,在 JS 引擎真正执行代码以前,编译过程当中,编译器就会将变量先在做用域内声明好。token
待解决:上面用的是 var,那么我用 let 声明变量的话,他也会帮我提早在做用域内声明好吗?作用域
代码编译完成后,就是执行步骤了,执行是由 JS 引擎执行,在执行 var a = 2
这行代码时,也会涉及到做用域:字符串
便于理解,接下来咱们把它们拟人化,每一个人工做上都有本身的工做职责,他们也是同样,对于做用域来讲,他的职责就是管理他这块区域的变量,这个区域里,存在哪些变量,变量分别存储的值是多少他都知道,也是他应该知道的,因此在以前的编译过程当中,编译器在声明 a 以前,首先跑去问了做用域确认这块区域里尚未变量 a 后,才让做用域在这块区域内声明了变量 a。
而编译完成后,也就是代码执行过程,JS 引擎这时看到 var a = 2
后,也会先去问做用域,在当前做用域下,是否已经存在变量 a 了呢?
做用域检查了下当前的他这块区域内的变量,回答说,“嗯嗯,已经有了,是刚刚编译的时候编译器声明的”。
JS 引擎:“好嘞,既然有了,那我就不用重复声明了,我就赋个值就好了。”
LHS && RHS
JS 引擎在向做用域询问变量的时候,查询的方式还能够细分为 LHS 和 RHS,也就是让做用域查询这个变量是否存在,仍是让做用域查询这个变量的值。
var a = 2 console.log(a)
代码如上,仍是以对话的形式
JS 引擎:做用域大哥,帮我看看你那里有没有变量 a 啊,我得给他赋个值。(查看变量容器自己是否存在,属于 LHS 查询方式) 做用域:找到了!这家伙在我这 JS 引擎: 谢谢了,再帮我看看 console 变量的值呢,我找找他里面有没有 log 这个方法(查询 console 变量的值,属于 RHS 查询方式) 做用域:有的,console 的值给你了,你看看吧 JS 引擎:好嘞,谢谢,我看一下。。有 log 这个方法!做用域,再帮我看下 a 变量的值呢,虽然我刚刚给他赋值了一个 2,可是我仍是得确认一下(查询变量 a 的值,属于 RHS 查询方式) 做用域:嗯嗯,我看了下,他的值仍是2 JS 引擎:好的,谢谢!
有的时候,做用域是嵌套的
// 最外层为全局做用域 var a = 2 function test() { // 对于 JS 来讲,一个函数会生成一个做用域(先不提 let 生成的块级做用域) console.log(a) } test()
对于 test 函数来讲,他本身内部就有一个做用域 A,最外层有一个全局做用域。
JS 引擎在调用 test 函数的时候,因为须要打印变量 a,所以向 test 函数内部的做用域 A 求助,问他有没有看到变量 a,做用域 A 说没有看到,因而 JS 引擎向做用域 A 外层的做用域(全局做用域)求助,最终在全局做用域里找到了变量 a。
有时候并不必定当前做用域的外层做用域就是全局做用域,可能还嵌套有其余做用域,可是查询方式都是同样的,当前做用域找不到,就沿着嵌套的做用域往外找,直到找到全局做用域。
若是在全局做用域内也找不到这个变量呢?
那就可能会报错了,可是具体的报错信息仍是有区别的:
查询变量自己(LHS 查询方式)
console.log.test.sss
,从一个 undefined
的数据类型上获取 sss
属性,就会抛出 typeError 错误