阅读 955

《JavaScript高级程序设计(第四版)》精读(四)第4章

这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战

  • 📢 大家好,我是小丞同学,最近在刷红宝书,这是一篇学习笔记
  • 📢 愿你我一起在这肆意生活里大放光彩
  • 这是阅读《JavaScript高级程序设计(第四版)》的第四天,本书已阅读 102/865

第四章:变量、作用域与内存

4.1 原始值与引用值

  • 原始值:最简单的数据。保存原始值的变量是按值访问的,因为我们操作的就是存储在变量中的实际值;
  • 引用值:由多个值构成的对象。保存引用值的变量是按引用访问的。

在操作对象时,实际上操作的是对该对象的引用而非实际的对象本身

4.1.1 动态属性

对于引用值而言,可以随时添加、修改和删除其属性和方法。

let person = new Object(); 
person.name = "Nicholas"; 
console.log(person.name); // "Nicholas"
复制代码

原始值不能添加属性,只有引用值可以动态添加后面可以使用的属性

4.1.2 复制值

额...

let num1 = 5; 
let num2 = num1;
复制代码

两个变量独立使用,互不干扰

对于引用值而言,复制值得操作可以理解为复制了内存的引用,两个变量指向的是同样一块内存空间

4.1.3 传递参数

在按值传递参数时,值会被复制到一个局部变量(即一个命名参数)。

在按引用传递参数时,值在内存中的位置会被保存在局部变量中,对本地变量的修改会反映到函数外部

4.1.4 确定类型

采用typeof操作符用于判断一个变量是否为原始值。可以判断字符串、数值、布尔值或布尔值或 undefined。

如果值为 null 则返回 object

如果需要进一步判断是什么类型的对象,可以采用instanceof

result = variable instanceof constructor
复制代码

如果用 instanceof 检测原始值,则返回 false

typeof 用来检测函数时返回 function

4.2 执行上下文与作用域

执行上下文:当js脚本执行时,执行环境会自动创建一个上线文栈,用于保存当前执行的环境,运行函数时会建立一个内部对象,也叫执行期上下文

关于这部分,这篇文章可以学习一下,执行上下文和作用域的理解

var color = "blue"; 
function changeColor() { 
 let anotherColor = "red"; 
 function swapColors() { 
 let tempColor = anotherColor; 
 anotherColor = color; 
 color = tempColor; 
 // 这里可以访问 color、anotherColor 和 tempColor 
 } 
 // 这里可以访问 color 和 anotherColor,但访问不到 tempColor 
 swapColors(); 
} 
// 这里只能访问 color 
changeColor();
复制代码

对于上面的代码,作用域链可以表示为:

image-20210716181308088

执行上下文与作用域的区别:

  1. 作用域是静态的,只要函数定义好,就一直存在,不再改变,执行上下文是动态的,调用函数时创建,函数调用结束时摧毁
  2. 产生的时间不同,函数执行上下文是在函数执行的前一刻确定的

联系:

  1. 全局上下文环境 -> 全局作用域
  2. 函数上下文环境 -> 对应的函数使用域

函数参数认为是当前上下文中的变量,因此与上下文中的其他变量遵循相同的访问规则

4.2.1 作用域链增强

  • try / catch 语句的 catch 块
  • with 语句
function buildUrl() { 
 let qs = "?debug=true"; 
 with(location){ 
 let url = href + qs; 
 } 
 return url; 
}
复制代码

例如书上的这个例子,执行函数时,实际上 href 实际上访问的是,location 对象下的 href 属性。原因在于,with 语句将 location 添加到了作用域链前端,因此 href 在访问时能够找到该属性

不推荐使用,影响正常判断

4.2.2 变量声明

三个至关重要的关键字,varlet以及const

var对我来说已经很少用了

1. 使用 var 的函数作用域声明

使用var 声明变量时,变量会被自动添加到最接近的上下文,未声明直接初始化的变量会添加到全局作用域

未经声明而初始化变量是JavaScript 编程中一个非常常见的错误,会导致很多问题。

2. 使用 let 的块级作用域声明

let 声明的变量作用域是块级的,变量只在最近的花括号内有效

if(true) {
    let a; //仅在块内有效
}
复制代码

与 var 的另一个不同在于,同一个作用域内不能重复声明两次

重复的let 声明会抛出SyntaxError错误

3. 使用 const 的常量声明

对于不变的量采用 const 关键字来声明,使用 const 声明的变量必须初始化

const 声明只应用到顶级原语或对象,也就是说将对象赋值给 const 声明的变量,不能重新赋予引用值,但是可以改变引用值的属性

const o1 = {};
o1 = {}; // TypeError: 给常量赋值
const o2 = {};
o2.name = 'Jake';
console.log(o2.name); // 'Jake'
复制代码

强烈建议,对于不变的值采用 const 声明

4. 标识符查找

在读取或者写入一个标识符时,从作用域链前端开始,一直搜索到全局上下文的变量对象,找到停止,找不到报错

4.3 垃圾回收

相对于C语言啥的,很轻松,自己会回收哈哈

4.3.1 标记清理

当变量进入环境时,将这个变量标记为“进入环境”。当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存。

工作流程:

  1. 垃圾回收器在运行的时候会给存储在内存中的所有变量都加上标记。

  2. 去掉环境中的变量以及被环境中的变量引用的变量的标记。

  3. 仍然带有标记的会被视为准备删除的变量。

  4. 垃圾回收器完成内存清除工作,销毁那些带标记的值并回收他们所占用的内存空间。

4.3.2 引用计数

原理:跟踪每个值被引用的次数

流程:

  1. 声明一个变量并将一个引用类型的值赋值给这个变量,这个引用类型值的引用次数就是1

  2. 同一个值又被赋值给另一个变量,这个引用类型值的引用次数加 1

  3. 当包含这个引用类型值的变量又被赋值成另一个值了,那么这个引用类型值的引用次数减1

  4. 当引用次数变成0时,说明没办法访问这个值了

  5. 当垃圾收集器下一次运行时,它就会释放引用次数是0的值所占的内存

感觉不够深入

4.3.3 内存泄漏

几种操作会引起内存泄漏

  1. 全局变量
  2. 闭包
  3. 没有清除DOM元素引用
  4. 遗忘的定时器或者回调
  5. console.log
文章分类
前端
文章标签