【JS高级程序第四版阅读笔记】第四章 变量、作用域和内存

JavaScript变量是松散类型的,而且变量不过就是特定时间点一个特定值的名称而已,变量的值和数据类型在脚本生命周期内可以改变。

原始值和引用值

  • 原始值: 最简单的数据,保存在栈内存上;
  • 引用值: 多个值构成的对象,存储在堆内存上。

原始值UndefinedNullBooleanNumberStringSymbol的变量是按值访问的,即我们操作的就是存储在变量中的实际值。

引用值是保存在内存中的对象。JS不运行直接访问内存位置,因此不能直接操作对象所在的内存空间。在操作对象时实际上操作的是对该对象的引用而非实际对象本身。对象值是按引用访问的。

动态属性

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

复制值

原始值复制

对于原始值复制,可以通过变量把一个原始值赋值给另一个变量,即:

let num1 = 5;
let num2 = num1;

这两个变量可以独立使用,互不干扰。

引用值复制

在把引用值从一个变量赋值给另一个变量时,即两个变量都指向了存储在堆内存中的对象,两个变量实际上指向了同一个对象:

let obj1 = new Object();
let obj2 = obj1;
obj1.name = 'sugarMei';
console.log(obj2.name);     // sugarMei

传递参数

ECMAScript中所有函数的参数都是按值传递的。这意味着函数外的值会被复制到函数内部的参数中,如果是原始值,那么就跟原始值变量的复制一样,如果是引用值,那么就跟引用值变量的复制一样。

确定类型

  • typeof适合来判断一个类型是否为原始类型;
  • instanceof可以判断一个对象是什么类型的对象。

执行上下文

变量或函数的上下文决定了它们可以访问哪些数据,以及它们的行为。每一个上下文都有一个关联的变量对象,而上下文中定义的所有变量和函数都存在于这个对象上。在全局上下文中这个对象就是window

执行上下文主要有全局上下文、函数上下文和块级上下文两种。

上下文中的代码在执行时,会创建变量对象的一个作用域链。这个作用域链决定了各级上下文中的代码在访问变量和函数时的顺序。

作用域链增强

某些语句会导致在作用域链前端临时添加一个上下文,这个上下文在代码执行后会被删除。通常在两种情况下会出现这个现象,即代码执行到下面任意一种情况时:

  • try/catch语句的catch
  • with语句

垃圾回收

JavaScript是门垃圾回收的语言,也就是说执行环境负责在代码执行时管理内存。思路简单而言就是:确定哪个变量不会再使用,然后释放它占用的内存。这个过程是周期性的,即垃圾回收程序每隔一定时间(或者说在代码执行过程中某个预定的收集时间)就会自动运行。

标记清理

JavaScript最常用的垃圾回收策略是标记清理(mark-and-sweep)。当变量进入上下文,比如在函数内部声明一个变量时,这个变量会被加上存在于上下文中的标记。而在上下文中的变量,逻辑上讲,永远不应该释放它们的内存,因为只要上下文中的代码在运行,就有可能用到它们。当变量离开上下文时,也会被加上离开上下文的标记。垃圾回收程序运行的时候,会标记内存中存储的所有变量(记住,标记方法有很多种)。然后,它会将所有在上下文中的变量,以及被在上下文中的变量引用的变量的标记去掉。在此之后再被加上标记的变量就是待删除的了,原因是任何在上下文中的变量都访问不到它们了。随后垃圾回收程序做一次内存清理,销毁带标记的所有值并收回它们的内存。

引用计数

另一种没那么常用的垃圾回收策略是引用计数(reference counting)。其思路是对每个值都记录它被引用的次数。声明变量并给它赋一个引用值时,这个值的引用数为1。如果同一个值又被赋给另一个变量,那么引用数加1。类似地,如果保存对该值引用的变量被其他值给覆盖了,那么引用数减1。当一个值的引用数为0时,就说明没办法再访问到这个值了,因此可以安全地收回其内存了。垃圾回收程序下次运行的时候就会释放引用数为0的值的内存。

性能

垃圾回收程序会周期性运行,如果内存中分配了很多变量,则可能造成性能损失,因此垃圾回收的时间调度很重要。尤其是在内存有限的移动设备上,垃圾回收有可能会明显拖慢渲染的速度和帧速率。开发者不知道什么时候运行时会收集垃圾,因此最好的办法是在写代码时就要做到:无论什么时候开始收集垃圾,都能让它尽快结束工作。

内存管理

在使用垃圾回收的编程环境中,开发者通常无须关心内存管理。不过,JavaScript运行在一个内存管理与垃圾回收都很特殊的环境。分配给浏览器的内存通常比分配给桌面软件的要少很多,分配给移动浏览器的就更少了。这更多出于安全考虑而不是别的,就是为了避免运行大量JavaScript的网页耗尽系统内存而导致操作系统崩溃。这个内存限制不仅影响变量分配,也影响调用栈以及能够同时在一个线程中执行的语句数量。

将内存占用量保持在一个较小的值可以让页面性能更好。优化内存占用的最佳手段就是保证在执行代码时只保存必要的数据。如果数据不再必要,那么把它设置为null,从而释放其引用。这也可以叫作解除引用。这个建议最适合全局变量和全局对象的属性。局部变量在超出作用域后会被自动解除引用。

  • 通过constlet声明提升性能;
  • 隐藏类和删除操作;
  • 内存泄漏;
  • 静态分配与对象池

猜你喜欢

转载自blog.csdn.net/qq_34086980/article/details/109097306