Post

JavaScript作用域和内存

什么是作用域?

作用域其实是一套规则

作用域可以相互嵌套,一个作用域中可以包含多个作用域;

作用域不能重叠。

作用域的作用是什么?

限定函数和变量的适用范围。

JS的本质是一门编译语言。

编译语言

编译语言分为两个部分,

第一是编译,编译包含词法和语法的分析和生成代码(这个才是最后运行的代码)

第二是运行

JS的编译特点

JS的编译是在浏览器中进行的,准备说是解释器中。

通过下面一个例子,解释下具体是怎么运行的?

var a = 123;

当打开js的一瞬间,js就开始编译,首先它会创造一个全局作用域,然后进入词法和语法分析,它可以分辨出哪些是语句,运算符,数据类型。

如果发现了一个声明语句,需要声明一个变量a,它首先会去查找当前的作用域是否有a这个变量,如果有,忽略,如果没有,会顺着作用域链查找,如果还是没有找到,会创建一个全局变量a,并分配一定的内存空间。词法和语法分析完后,会生成一段代码。

这也是为什么变量的声明会被提前,它先有一个编译的过程。

如果声明的不是一个变量,而是一个函数,声明会被提前,然后会安排一个作用域。

这里有个问题, 你声明了一个函数和一个变量,他们的名称是一样的,那么在当前作用域下,函数会覆盖变量。

综上所述

编译过程分解
  1. 创建作用域(如果在全局运行就创建全局作用域,在函数中运行就创建函数作用域)
  2. 在当前作用域下创建一个对象(活动对象)
  3. 创建名称a
  4. 询问活动对象是否存在a
  5. 如果存在则忽略,如果不存在则创建a并添加到活动对象中
  6. 生成可执行代码
执行过程分解
  1. 创建作用域链,链接当前作用域下的活动对象和外层作用域的活动对象
  2. 询问当前活动对象是否有a
  3. 如果有则设置为123,如果没有则顺着作用域到外层作用域下查找,以此类推
  4. 如果查找到全局作用域还没找到,则在全局作用域下创建属性a,并赋值为1
作用域链

作用域是定义存储的规则,作用域链是定义访问的规则

生成代码执行的过程中,会创建一个作用域链

作用域链的作用是对作用域中的函数和变量进行访问

也就是说作用域是存储的规则,作用域链是访问的规则

每次进入一个函数中,多会创建一个作用域链。

作用域链的查找,只能去父级查找,不能去同级查找。

并且它是一个单向的过程。

所以在JS中只有函数作用域,没有块级作用域,因为它进入一个函数中,会创建一个作用域。

JS没有块级作用域,这也是为什么,循环结束后,你仍能在循环体外面访问岛变量。

如何使用块级作用域?

  1. 把块级作用域做成函数作用域
  2. es6支持块级作用域(使用let,声明的变量仅在块级作用域内有效)
with和eval

JS解释器在运行的过程中会进行大量的优化来提升性能

在代码中应尽量避免使用with和eval,因为它会破坏性能。

他们破坏解释器的作用域规则,那么解释器在执行的过程中对它们不会进行优化。

垃圾回收机制

目的: 保证内存的合理使用

JS具有自动垃圾收集机制。

JS在运行过程中,知道有哪些变量,他的作用域范围,在执行完成后会讲不需要的销毁掉。

JS 垃圾回收不是在函数执行完后,立即运行,他是一次一次运行的。

大部分浏览器在运行的过程中,会将没有引用关系的变量释放掉。

你可以手动释放,将不使用的变量赋值null就行。

JS在下次运行中,就会知道这个变量是不需要的。

This post is licensed under CC BY 4.0 by the author.