《JavaScript高级程序设计(第四版)》学习笔记(四)第4章

《JavaScript高级程序设计(第四版)》学习笔记(四)第4章

???? 大家好,我是小丞同学,最近在刷红宝书,这是一篇学习笔记

???? 愿你我一起在这肆意生活里大放光彩

???? 这是阅读《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();

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

《JavaScript高级程序设计(第四版)》学习笔记(四)第4章

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


作用域是静态的,只要函数定义好,就一直存在,不再改变,执行上下文是动态的,调用函数时创建,函数调用结束时摧毁

产生的时间不同,函数执行上下文是在函数执行的前一刻确定的

联系:


全局上下文环境 -> 全局作用域

函数上下文环境 -> 对应的函数使用域

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


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


上一篇:【权威榜单】2017 年中国高被引学者榜单发布,计算机科学领域 153 位学者入选


下一篇:详细解析那些万里挑一但实用的PS使用技巧