🤣一个于JS作用域的基础问题
从作者的提问可以了解到的是,作者对于JS作用域没有一个清晰的概念。下面先交代一下JS的作用域是什么吧!
作用域
Java Script引擎在执行JavaScript代码时,需要用到编译器、执行引擎和作用域。
* 编译器:语法分析、代码生成等功能。
* 执行引擎:从头到尾负责整个程序的编译及执行过程。
* 作用域:负责收集并维护所有标识符组成的一系列查询,并确定当前执行代码对这些标识符的访问权限。
动态作用域VS静态作用域
在编程语言中可以将作用域分为动态作用域 和静态作用域
* 动态作用域:在运行时 确定作用域关系
* 静态作用域:在编译时 确定作用域关系
JS的作用域属于静态作用域,简单理解就是,作用域关系是在代码编写的时候就已经确定了的 ,无论在哪里调用和执行都不会变。
JS中的作用域
首先,在js中的作用域分为全局作用域、函数作用域和块级作用域。在之前js只存在全局作用域和函数作用域,ES6之后引入了块级作用域。而块级作用域只适用于"const"和"let"声明的变量,所以这里我们不需要考虑块级作用域。
// 这里是全局作用域
function test() {
// 这里是函数作用域
}
在"js"中变量的查找是从当前的函数作用域开始向上查找的,也就是说如果在"test"中使用变量"a",那么会先在"test"的函数作用域查找,如果在"test"中存在变量"a"则停止查找。
回答问题
问题一
«为什么第一个可行,第二个不可行?»
function a(){
var io = "io"
mystart()
function mystart(){
console.log(io);
}
}
a()
这里涉及到了一个全局作用域和两个函数作用域,当执行"a()"时,函数"a"中声明了函数"mystart",也就是说"a"的函数作用域是"mystart"的父级作用域。
"image.png" (https://wmprod.oss-cn-shanghai.aliyuncs.com/images/20241126/38744b1172f8075c95f6af4d7da54f78.png)
当执行"mystart"时,"console.log(io)"会在当前的"mystart"作用域中查找"io"变量,发现"mystart"中不存在"io"变量后,会向"mystart"的父级作用域("a"函数作用域)查找,此时在"a"函数作用域中顺利找到"io"变量。于是顺利打印出""io""。
但是对于下面的代码:
function a(){
var io = "io"
mystart()
}
function mystart(){
console.log(io);
}
a()
这段代码与上面的不同之处在于,"mystart"的父级作用域不再是函数"a",而是全局作用域。
"image.png" (https://wmprod.oss-cn-shanghai.aliyuncs.com/images/20241126/396858b2c8c63eb94692725884aa4640.png)
这时候在执行"mystart"时,查找"io"变量就不会再去"a"函数作用域中查找了,由于全局作用域中没有"io"变量。那么"console.log(io)"就会查找不到"io"变量,那么抛出"ReferenceError"异常。
问题二
«为什么第二个访问io变量的时候不会按照访问规则当前函数作用域下访问不到,到嵌套着它的函数作用域中去寻找?»
之所以有这样的疑惑,是因为错误的将JS的作用域机制理解成了动态作用域导致的。
然而JS采用的是静态作用域规则,也就是说JS的作用域是在编写代码时就确定的
。将"mystart"写在全局作用域下,那么"mystart"的父级作用域就是全局作用域,而不是"mystart()"调用时所在的作用域("a"函数作用域)。
所以题主理解错了JS的作用域,才会奇怪为什么不会去嵌套作用域中寻找!
问题三
«两种写法的函数实际存储方式大概是怎样的?»
参考这两个图
"image.png" (https://wmprod.oss-cn-shanghai.aliyuncs.com/images/20241126/38744b1172f8075c95f6af4d7da54f78.png)
"image.png" (https://wmprod.oss-cn-shanghai.aliyuncs.com/images/20241126/396858b2c8c63eb94692725884aa4640.png)