夜听城嚣 夜听城嚣
首页
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 前端基建与架构
  • 专题分享

    • Git入门与开发
    • 前端面试题汇总
    • HTML和CSS知识点
  • 项目实践
  • 抓包工具
  • 知识管理
  • 工程部署
  • 团队规范
bug知多少
  • 少年歌行
  • 青年随笔
  • 文海泛舟
  • 此事躬行

    • 项目各工种是如何协作的
    • TBA课程学习
收藏

dwfrost

前端界的小学生
首页
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 前端基建与架构
  • 专题分享

    • Git入门与开发
    • 前端面试题汇总
    • HTML和CSS知识点
  • 项目实践
  • 抓包工具
  • 知识管理
  • 工程部署
  • 团队规范
bug知多少
  • 少年歌行
  • 青年随笔
  • 文海泛舟
  • 此事躬行

    • 项目各工种是如何协作的
    • TBA课程学习
收藏
  • 第1章 什么是JavaScript
  • 第2章 HTML中的JavaScript
  • 第3章 语言基础
  • 第4章 变量、作用域与内存
    • 第5章 基本引用类型
    • 第6章 集合引用类型
    • 第7章 迭代器与生成器
    • 第8章 对象、类与面向对象编程
    • 第9章 代理与反射
    • 第10章 函数
    • 第11章 异步编程
    • 第12章 BOM
    • 第14章 DOM
    • 第15章 DOM扩展
    • 第16章 DOM2和DOM3
    • 第17章 事件
    • 第18章 动画与Canvas图形
    • 第19章 表单脚本
    • 第20章 JavaScript API
    • 第21章 错误处理与调试
    • 第23章 JSON
    • 第24章 网络请求与远程资源
    • 第25章 客户端存储
    • 第26章 模块
    • 第27章 工作者进程
    • 第28章 最佳实践
    • 《JavaScript高级程序设计》
    frost
    2021-07-04

    第4章 变量、作用域与内存

    # 原始值与引用值

    变量分为 2 种:

    原始值也称为简单数据类型,包括 Undefined、Null、Boolean、Number、String 和 Symbol。保存原始值的变量是按值访问的。

    引用值也成为复杂数据类型,是保存在内存中的对象,细分为 Object、Array、Function 和 js 内置对象等。保存引用值的变量是按引用访问的。

    # 复制值

    变量 a 赋值给另一个变量 b 时,需要分情况讨论

    1.如果变量 a 是原始值,则原始值会复制一份到新变量上,此时对 a 和 b 的修改互不影响。

    let a = 5
    let b = a
    a++
    b--
    console.log(a, b) // 6 4
    
    1
    2
    3
    4
    5

    2.如果变量 a 引用值,则复制的 a 的引用地址,这个地址其实是指针,它指向存储在堆内存中的对象,因此实际上这 2 个变量指向的是同一个对象,此时修改 a 或 b 会同步在另一个变量反映出来。

    let a = { name: 'jack' }
    let b = a
    b.name = 'rose'
    console.log(a) // 'rose'
    a.name = 'mike'
    console.log(b) // 'mike'
    
    1
    2
    3
    4
    5
    6

    # 函数参数传递

    所有函数的参数都是按值传递的,而不是按引用传递。

    如果参数是按引用传递,则会认为传入的参数在函数内部被修改后,会影响到外部的变量。而实际上,函数参数的传递,可以理解为就是局部创建了新的变量,用来接收函数外部传入的值。如果这个值是原始值,则是将值复制;如果是引用值,则复制的是内存地址。用法同上一个小结【复制值】。

    let a = { name: 'jack' }
    function setName(obj, name) {
      obj.name = name
    }
    setName(a, 'rose')
    console.log(a.name) // 'rose'
    
    1
    2
    3
    4
    5
    6

    # 执行上下文与作用域

    执行上下文决定了访问数据的方式和行为,体现为如何搜索变量和当前执行环境的 this 代表什么(这一节没讲 this)。

    执行上下文包括 2 个方面的内容:变量对象和作用域。(this 也是一部分,但本书没罗列进来)

    变量对象:包括上下文中的函数和变量。在当前上下文中,当里面的代码执行完后,变量对象会被销毁。

    每次调用函数时,当前函数就会推到一个上下文栈,并执行该函数;函数执行完后上下文栈会弹出该函数上下文。这个过程中函数所在的上下文是一段作用域,这样形成了作用域嵌套作用域的关系,它决定了各级上下文如何访问变量和函数。也称为变量搜索原则,如下:

    访问一个变量时,从当前作用域中搜索,如果没有,则逐级向外层作用域中查找,直到全局变量。反过来则不行,即作用域链访问顺序只能从下级查找上级作用域,而不能反过来查找。

    # 垃圾回收

    js 不需要开发者手动管理内存,而是通过自动内存管理实现内存分配和闲置资源回收。过程是周期性的:确定哪个变量不再使用,则释放它占用的内存。

    如何标记未使用的变量,包括 2 类:标记清理和引用计数。

    # 标记清理

    当变量进入上下文,则标记为存在上下文中。

    当变量离开上下文,则标记为离开上下文。

    在上下文中的变量,访问权限仅限在本上下文,因此当上下文执行完时,这部分变量都不可被外界访问到了,会带上待删除的标记,随后被垃圾回收程序清理。

    # 引用计数

    思路是对每个值都记录它被引用的次数,比如声明一个变量,并对其赋值,则该值的引用数为 1;如果该变量指向了另一个值,那前一个值的引用数减 1。引用数为 0 的值,会被垃圾回收程序在下一次运行时释放该值的内存。

    问题:循环引用时,引用数永远不会变为 0,会造成内存泄漏。解决方法是,在合适的时机,赋值为 null,即切断变量和值的关系。

    浏览器中,垃圾回收的频率会影响渲染速度和帧速率,降低性能。频率高,则影响渲染性能;频率低,则内存不够用。

    局部变量在超出作用域后(如上下文执行完毕后)会自动解除引用;而全局变量需要手动解除,让其指向 null。

    # 内存泄漏

    原因可能如下:

    1.意外声明全局变量,比如没有关键字声明变量

    function setName() {
      name = 'jack'
    }
    
    1
    2
    3

    2.定时器未注销

    3.闭包

    # 静态分配与对象池

    尽量避免一次性实例化多个对象,如果可以,在函数调用之前就实例化,并将实例作为参数传入。即避免动态创建对象,让它直接使用已有的对象。

    此外,如果参数多,则使用对象池,即使用一组对象的属性来管理,程序可以向该对象池申请一个对象,在操作完成后再还给对象池。这样避免了频繁初始化对象,也就绕过了垃圾回收程序的检测。

    上次更新: 2021/09/20, 23:04:19
    第3章 语言基础
    第5章 基本引用类型

    ← 第3章 语言基础 第5章 基本引用类型→

    最近更新
    01
    提交代码时修改commit消息
    04-09
    02
    如何快速定位bug
    02-20
    03
    云端web项目开发踩坑
    08-25
    更多文章>
    Theme by Vdoing | Copyright © 2021-2025 dwfrost | 粤ICP备2021118995号
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式
    ×