序言
首先回顾一下上一章节:介绍了 JavaScript 的语法、变量声明、数据类型、操作符、语句还有函数。今天第四更底层的知识,做好笔记,开始。
MIND
原始值和引用值
ECMAScript 变量包括原始值和引用值两种,有以下区别:
- 构成:原始值就是最简单的数据,而引用值是多个值构成的对象
- 访问:保存原始值的变量是按值访问,操作存储在变量中的实际值;保存引用值的变量是按引用访问,实际操作的是对该对象的引用
- 动态属性:原始值不能有属性,而引用值有,而且可以动态添加、修改和删除属性(和方法)
- 复制值:变量赋给另一个变量时,两者都是将变量中的值复制到新变量所在的位置,但原始值复制的值是值的副本,而引用值是指向存储在堆内存中的对象的指针。
传递参数
无论原始值还是引用值,向函数传递参数时,都是按值传递,也就是说跟原始值和引用值的复制一样地传递方式-——将函数外的值复制到函数内部的参数。在 ECMAScript 中,函数的参数就是局部变量,因此,按值传递的值会被复制到一个局部变量。
确认类型
上一章节数据类型检测的操作符 typeof 用来检测原始值还行,可是遇到引用值就只会 "object" 这一套,连 null 都算进去。instanceof 就可以解决问题。
const colorList = ['green', 'yellow', 'cyan'];
console.log(colorList instanceof Array); // true
注意,用 instanceof 检测原始值返回 false
执行上下文
这是该章节最为重要的知识点,可能需要后面知识量贯通更好理解,这里仅仅讲述一些概念。
执行上下文(Execution Context),简称 EC
- 来源:JS 引擎执行可执行代码前,进行代码解析,创建执行上下文
- 含义:决定变量或函数可以访问哪些数据,还有决定它们的行为,是 JS 的执行环境
- 分类:全局执行上下文、函数执行上下文、eval 执行上下文(很少使用)
- 组成元素:变量对象(Variable object,VO)、作用域链(Scope chain)、this(后面章节介绍)
- 管理:执行上下文栈(Execution context stack,ECS)
执行上下文栈
- 全局执行上下文:最外层的上下文,同时也是执行上下文栈中最下层。任何不在函数内部的代码都会在全局上下文中
- 每当函数被调用,都会为该函数创建一个新的上下文
用来管理执行上下文,当 JS 开始解释执行代码时,首先遇到全局执行代码,初始化时将对应的全局执行上下文押人员栈底,等待程序执行完毕后才会清空。程序执行中遇到函数执行代码,创建函数执行上下文,压入执行上下文栈,等待函数执行完毕,遵从栈的后进先出规则,函数执行上下文会从栈中弹出
变量对象
变量对象存放上下文中所定义的所有变量和函数,无法通过代码访问,用于后台处理数据。
全局对象
全局上下文中的变量对象就是全局对象,根据不同的宿主环境而产生差异,在浏览器中,全局对象是 window 对象。全局对象是预定义的对象,作为 JS 的全局函数和全局属性的占位符,通过使用全局对象可以访问所有其他预定义的对象、函数和属性。
活动对象
函数执行上下文中,将活动对象(activation object,AO)作为变量对象。活动对象是进入函数执行上下文时被创建的,通过函数的 arguments 属性初始化。本身与边来那个对象是一个东西,但不同的是,活动对象是激活的变量对象,该对象上的各种属性可以访问。
作用域链
作用域链决定了各级上下文中的代码在访问变量和函数的顺序,其本质是一个变量对象的指针列表,其中全局变量始终位于作用域链的最前端。当查找变量时,会从当前执行上下文的变量对象中查找,如果没有找到,就会从父上下文的变量对象中查找,直到找到全局变量。
作用域链增强
除了三种执行上下文外,可通过在作用域链前端临时添加上下文来增强作用域链,通常有两种方式:
- try/catch 语句的 catch 语句
- with 语句
推荐
这部分需要好好理解,推荐以下文章细细咀嚼:
- 冴羽
- 上下文栈
- 变量对象
- 作用域链
- 执行上下文
- cavszhouyou
- 执行上下文
- 变量对象
- 作用域链
- 执行上下文示例分析
作用域
变量提升
var 声明变量时,变量会自动添加到最接近的上下文,即全局上下文或函数上下文中,被拿到全局作用域或函数作用域的顶部,位于作用域中所有代码之前,形成变量提升
暂时性死区
let 声明变量其实也存在变量提升,但它形成的块级作用域有暂时性死区,不能在声明前使用 let 声明的变量,因此看起来变量没有提升。
垃圾回收
JS 通过自动内存管理实现内存分配和闲置资源回收。
- 思路:确定不再使用的变量,然后释放该变量所占内存。
- 缺陷:难以判定变量是否不再使用
- 解决:采用标记策略跟踪变量是否还会使用,主要有:标记清理和引用计数
标记清理
垃圾回收时,首先标记内存中存储的所有变量,然后将所有在上下文中的变量,以及这些变量引用的变量的标记去除。最后垃圾回收程序进行内存清理,销毁带标记的所有值并收回它们的内存。
引用计数
不常用的策略。思路是对每个值都记录它被引用的次数,初始声明时次数为 1,该值随保存该值的变量而变动,当变量被引用赋值加 1,被其他值覆盖时减 1。如果引用值为 0,则在垃圾回收时释放该值内存
性能
垃圾回收程序的周期性运行会影响到程序的运行,造成性能损失。各个浏览器引擎会基于 JS 运行时环境的探测来决定何时运行,各有差异。其中 Chrome 的 V8 是根据活跃对象的数量增加一些余量,而 IE 是分配固定的数目,性能不堪。
内存管理
为了避免运行大量 JS 导致网页耗尽内存,浏览器所分配到的内存是限制住的。需要优化内存占用:
- 解除引用:不需要的数据,设置为 null,等待释放
- 声明优化:不使用 var,优先使用 const,然后是 let,后两者能更快进入到垃圾回收
- 隐藏类:避免通过隐藏类动态的添加属性赋值,在构造函数中提前一次性声明
- 避免内存泄漏:避免意外声明全局变量、定时器设有关闭语句、谨慎使用闭包
- 静态分配和对象池:静态分配是极端形式,不考虑。对象池策略是创建对象池来管理可回收对象,不需要初始化对象,避开垃圾回收检测,因此不会频繁触发垃圾回收程序
总结
这一章笔记很空洞,emmmm,但都是很重要的内容,需要多看看其他文章,或者说红宝书其他章节都知晓的情况下更容易总结。所以,下一章:基本引用类型。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!