JavaScript作用域和内存
什么是作用域?
作用域其实是一套规则
作用域可以相互嵌套,一个作用域中可以包含多个作用域;
作用域不能重叠。
作用域的作用是什么?
限定函数和变量的适用范围。
JS的本质是一门编译语言。
编译语言
编译语言分为两个部分,
第一是编译,编译包含词法和语法的分析和生成代码(这个才是最后运行的代码)
第二是运行
JS的编译特点
JS的编译是在浏览器中进行的,准备说是解释器中。
通过下面一个例子,解释下具体是怎么运行的?
var a = 123;
当打开js的一瞬间,js就开始编译,首先它会创造一个全局作用域,然后进入词法和语法分析,它可以分辨出哪些是语句,运算符,数据类型。
如果发现了一个声明语句,需要声明一个变量a,它首先会去查找当前的作用域是否有a这个变量,如果有,忽略,如果没有,会顺着作用域链查找,如果还是没有找到,会创建一个全局变量a,并分配一定的内存空间。词法和语法分析完后,会生成一段代码。
这也是为什么变量的声明会被提前,它先有一个编译的过程。
如果声明的不是一个变量,而是一个函数,声明会被提前,然后会安排一个作用域。
这里有个问题, 你声明了一个函数和一个变量,他们的名称是一样的,那么在当前作用域下,函数会覆盖变量。
综上所述
编译过程分解
- 创建作用域(如果在全局运行就创建全局作用域,在函数中运行就创建函数作用域)
- 在当前作用域下创建一个对象(活动对象)
- 创建名称a
- 询问活动对象是否存在a
- 如果存在则忽略,如果不存在则创建a并添加到活动对象中
- 生成可执行代码
执行过程分解
- 创建作用域链,链接当前作用域下的活动对象和外层作用域的活动对象
- 询问当前活动对象是否有a
- 如果有则设置为123,如果没有则顺着作用域到外层作用域下查找,以此类推
- 如果查找到全局作用域还没找到,则在全局作用域下创建属性a,并赋值为1
作用域链
作用域是定义存储的规则,作用域链是定义访问的规则
生成代码执行的过程中,会创建一个作用域链
作用域链的作用是对作用域中的函数和变量进行访问
也就是说作用域是存储的规则,作用域链是访问的规则
每次进入一个函数中,多会创建一个作用域链。
作用域链的查找,只能去父级查找,不能去同级查找。
并且它是一个单向的过程。
所以在JS中只有函数作用域,没有块级作用域,因为它进入一个函数中,会创建一个作用域。
JS没有块级作用域,这也是为什么,循环结束后,你仍能在循环体外面访问岛变量。
如何使用块级作用域?
- 把块级作用域做成函数作用域
- es6支持块级作用域(使用
let
,声明的变量仅在块级作用域内有效)
with和eval
JS解释器在运行的过程中会进行大量的优化来提升性能
在代码中应尽量避免使用with和eval,因为它会破坏性能。
他们破坏解释器的作用域规则,那么解释器在执行的过程中对它们不会进行优化。
垃圾回收机制
目的: 保证内存的合理使用
JS具有自动垃圾收集机制。
JS在运行过程中,知道有哪些变量,他的作用域范围,在执行完成后会讲不需要的销毁掉。
JS 垃圾回收不是在函数执行完后,立即运行,他是一次一次运行的。
大部分浏览器在运行的过程中,会将没有引用关系的变量释放掉。
你可以手动释放,将不使用的变量赋值null就行。
JS在下次运行中,就会知道这个变量是不需要的。