*本文是在看了【js 进阶】全篇干货 !一篇文章让你彻底弄懂栈、堆、队列、执行栈、上下文、事件循环(Event Loop),之后的读后感。
- js数据类型
- 栈内存 堆内存 调用栈 函数的[[scope]]属性与作用域链
- 闭包&chrome查看闭包
js数据类型
原始类型
- number
- string
- boolean
- undefined
- symbol(es10-2019)
- bigint(es11-2020)
- null
typeof null // Object,是因为js中Object地址是以000开头,而null为全0
引用类型(唯一)Object
Object的子集:
- Fucntion
Fucntion.prototype.__proto__ === Object.prototype - Array
Array.prototype.__proto__ === Object.prototype - Map
Map.prototype.__proto__ === Object.prototype - Set
Set.prototype.__proto__ === Object.prototype - WeakMap
WeakMap.prototype.__proto__ === Object.prototype - WeakSet
WeakSet.prototype.__proto__ === Object.prototype - WeakRef(es12-2021)
WeakRef.prototype.__proto__ === Object.prototype - Math
Math.__proto__ === Object.prototype - 特殊引用类型 Number String Boolean
堆栈与执行上下文
- 栈(Stack):先入后出。js的栈内存用来存放原始数据类型及引用类型的指针
- 堆(Heap): 以key-value形式存放数据,无序。js的堆内存中存放引用类型的值
在js执行时,会将待执行的函数依次进入js引擎的执行栈,执行完后再出栈,此时函数内部创建的基本类型数据会保存到栈中,引用类型数据的指针也会保存到栈中。执行完后如果不存在闭包问题,就会出栈(销毁该函数的调用栈)。
执行栈也称调用栈或执行上下文栈,可以在chrome中查看
闭包(Closure)
上面的例子,如果存在闭包例如下面这样。函数func1执行完毕出栈后,内部创建变量a应该被销毁,但是因为在func1内部创建的函数func2,使用到了func1中声明的变量,导致,及时func1的调用栈销毁了,但是因为创建func2的时候,js是将func2函数与其所在的词法作用域捆绑在一起的。所以变量a可以在func2的作用域链上找到。
Scope:是func2函数的[[scope]]属性,该属性是函数的隐藏属性,仅js引擎可访问。
Local:func2的上下文内可访问变量,里面没有a
Closure:func2被声明时的词法环境,此时因为func2中使用了func1中声明的变量,所以虽然func1的调用栈被销毁了,但是这部分数据内容(a)从func1的调用栈,转移到func2的[[scope]]属性上,使func2可以访问。
Global:为全局上下文
闭包:是由函数以及声明改函数的词法环境组合成的,闭包可以让你在一个内层函数中访问到外层函数的作用域。每当创建一个函数,闭包就会在函数创建的同时被创建出来。——————MDN
PS: 如果func2没有使用到func1中声明的变量,就不会将func1的部分词法环境(用词可能不准确)添加到func2的作用域链上,见下图。
说到底,还是要真正的理解作用域链[[scope]],闭包只是作用域链上的一个节点或几个节点