聊一聊:变量,作用域与内存
数据类型
js数据类型分两种:
- 基本数据类型:Undefined、 Null、 String、 Boolean、 Number、 String 和 Symbol(ES6新增)
- 复杂数据类型:Object
区别:内存的分配不同
- 基本数据类型存储在栈中
- 复杂数据类型存储在堆中,栈中存储的变量,是指向堆中的引用地址
区分数据类型的方法:
- typeof:可以确定值是否是基本数据类型
- instanceof:可以确定值是否是引用数据类型用
1
2typeof null // object
null instanceof Object // false
区分数组和Object
1 | // [Object, objct]、[Object, array] |
null是一个对象吗?
另外尽管
typeof null === 'object'
,但null 不是一个对象,这是一个历史遗留问题,JS 的最初版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000 开头代表是对象,null 表示为全零,所以将它错误的判断为 object 。
作用域链
内部上下文可以通过作用域链访问外部上下文的一切,但外部上下文无法访问内部上下文中的东西。找一个变量,如果在局部找到,则搜索停止;否则,继续沿着作用域链向上搜(作用域链中的对象有原型链,因此搜索也可能涉及每一个对象的原型链),这个过程一直延续到全局上下文的变量对象;若仍找不到,则未声明。
js执行上下文
执行上下文就是当前 JavaScript 代码被解析和执行时所在环境的抽象概念, JavaScript 中运行任何的代码都是在执行上下文中运行。
执行上下文分全局上下文、函数上下文和块级上下文
JS执行上下文栈(后面简称执行栈)
执行栈,也叫做调用栈,具有 LIFO (后进先出) 结构,用于存储在代码执行期间创建的所有执行上下文。
规则如下:
首次运行JavaScript代码的时候,会创建一个全局执行的上下文并Push到当前的执行栈中,每当发生函数调用,引擎都会为该函数创建一个新的函数执行上下文并Push当前执行栈的栈顶。
当栈顶的函数运行完成后,其对应的函数执行上下文将会从执行栈中Pop出,上下文的控制权将移动到当前执行栈的下一个执行上下文。
以一段代码具体说明:
1 | function fun3() { |
Global Execution Context
(即全局执行上下文)首先入栈,过程如下:
伪代码:
1 | //全局执行上下文首先入栈 |
块级作用域的概念
由最近的一对{}界定
var、let 和 const
- var存在变量的提升
- let也存在变量的提升,但因存在“暂时性死区”,故不能在声明前使用let变量
- let与var不同在于,同一作用域内let不能重复声明,let有块级作用域;let在全局作用域声明的变量不会成为window的属性
- const和let基本相同,除了一点,声明时必须初始化变量,且不能修改;但可以修改const对象的属性(
const a = Object.freeze({})
可以冻结)
js变量是松散类型,意思是可以保存任何类型的数据
内存管理
优化内存的手段:
- 数据不再用,赋值null,释放其引用
- let和const有块级作用域,有助于垃圾回收