???? 大家好,我是小丞同学,最近在刷红宝书,这是一篇学习笔记
???? 愿你我一起在这肆意生活里大放光彩
???? 这是阅读《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();
对于上面的代码,作用域链可以表示为:
执行上下文与作用域的区别:
作用域是静态的,只要函数定义好,就一直存在,不再改变,执行上下文是动态的,调用函数时创建,函数调用结束时摧毁
产生的时间不同,函数执行上下文是在函数执行的前一刻确定的
联系:
全局上下文环境 -> 全局作用域
函数上下文环境 -> 对应的函数使用域
函数参数认为是当前上下文中的变量,因此与上下文中的其他变量遵循相同的访问规则
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 变量声明
三个至关重要的关键字,var、let以及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 标记清理
当变量进入环境时,将这个变量标记为“进入环境”。当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存。
工作流程:
垃圾回收器在运行的时候会给存储在内存中的所有变量都加上标记。
去掉环境中的变量以及被环境中的变量引用的变量的标记。
仍然带有标记的会被视为准备删除的变量。
垃圾回收器完成内存清除工作,销毁那些带标记的值并回收他们所占用的内存空间。
4.3.2 引用计数
原理:跟踪每个值被引用的次数
流程:
声明一个变量并将一个引用类型的值赋值给这个变量,这个引用类型值的引用次数就是1
同一个值又被赋值给另一个变量,这个引用类型值的引用次数加 1
当包含这个引用类型值的变量又被赋值成另一个值了,那么这个引用类型值的引用次数减1
当引用次数变成0时,说明没办法访问这个值了
当垃圾收集器下一次运行时,它就会释放引用次数是0的值所占的内存
感觉不够深入
4.3.3 内存泄漏
几种操作会引起内存泄漏
全局变量
闭包
没有清除DOM元素引用
遗忘的定时器或者回调
console.log