第 04 章 变量、作用域与内存
1、原始值与引用值
原始值(primitive value)就是 最简单的数据,引用值(reference value)则是由多个值构成的对象。
保存原始值的变量是按值(by value)访问的,引用值是按引用(by reference)访问的。
ECMAScript 中所有函数的参数都是按值传递的。
typeof 判断原始值
instanceof 判断引用值 原始值始终返回false
2、执行上下文与作用域
let 暂时性死区
typeof a; //"undefined"
typeof a; // VM446:1 Uncaught ReferenceError: a is not defined
let a = 2;
复制代码
3、垃圾回收
标记清理(mark-and-sweep)
引用计数 (reference counting) - 循环引用
性能
回收时间
IE7 发布后,JavaScript 引擎的垃圾回收程序被调优为动态改变分配变量、字面量或数组槽位等会触 发垃圾回收的阈值。IE7的起始阈值都与IE6的相同。如果垃圾回收程序回收的内存不到已分配的15%,这些变量、字面量或数组槽位的阈值就会翻倍。如果有一次回收的内存达到已分配的 85%,则阈值重置为默认值。这么一个简单的修改,极大地提升了重度依赖 JavaScript 的网页在浏览器中的性能。
内存管理
优化内存占用的最佳手段就是保证在执行代码时只保存必要的数据。如果数据不再必要,那么把它设置为 null,从而释放其引用。这也可以叫作解除引用。
1、通过 const 和 let 声明提升性能
2、隐藏类和删除操作
运行期间,V8 会将创建的对象与隐藏类关联起来,以跟踪它们的属性特征
避免 JavaScript 的“先创建再补充”(ready-fire-aim)式的动态属性赋值,并在构造函数中一次性声明所有属性
使用 delete 关键字会导致生成相同的隐藏类片段,最佳实践是把不想要的属性设置为 null
3、内存泄漏
- 意外声明全局变量 (漏掉var/let 会导致声明全局变量)
- 定时器
- 闭包
4、静态分配与对象池
静态分配与对象池,提前创建对象池,以减少浏览器执行垃圾回收的次数,如下面的 resultant 由临时变量改为全局的对象池中的变量(大多数情况下,这都属于过早优化,因此不用考虑。)
function addVector(a, b) {
let resultant = new Vector();
resultant.x = a.x + b.x;
resultant.y = a.y + b.y;
return resultant;
}
复制代码
第 05 章 基本引用类型
引用值(或者对象)是某个特定引用类型的实例。引用类型有时候也被称为对象定义,因为它们描述了自己的对象应有的属性和方法。
对象被认为是某个特定引用类型的实例。新对象通过使用 new 操作符后跟一个构造函数(constructor) 来创建。
1、Date
Date.parse()方法接收一个表示日期的字符串参数,尝试将这个字符串转换为表示该日期的毫秒数。
- “月/日/年”,如"5/23/2019";
- “月名 日, 年”,如"May 23, 2019";
- “周几 月名 日 年 时:分:秒 时区”,如"Tue May 23 2019 00:00:00 GMT-0700";
- ISO 8601扩展格式“YYYY-MM-DDTHH:mm:ss.sssZ”,如2019-05-23T00:00:00(只适用于兼容 ES5 的实现)。
如果直接把表示日期的字 符串传给 Date 构造函数,那么 Date 会在后台调用 Date.parse()
let someDate = new Date("May 23, 2019");
复制代码
Date.UTC()
// GMT时间2005年5月5日下午5点55分55秒
let allFives = new Date(Date.UTC(2005, 4, 5, 17, 55, 55));
// 本地时间2005年5月5日下午5点55分55秒
let allFives = new Date(2005, 4, 5, 17, 55, 55);
复制代码
Date 类型的 valueOf()
方法根本就不返回字符串,这个方法被重写后返回的是日期的毫秒表示。因此,操作符(如小于号和大于号)可以直接使用它返回的值。
2、RegExp
let expression = /pattern/flags;
复制代码
pattern(模式)可以是任何简单或复杂的正则表达式,包括字符类、限定符、 分组、向前查找和反向引用。每个正则表达式可以带零个或多个 flags(标记),用于控制正则表达式的行为。
RegExp 构造函数的两个参数都是字 符串。因为 RegExp 的模式参数是字符串,所以在某些情况下需要二次转义。所有元字符都必须二次转义,包括转义字符序列,如\n (\转义后的字符串是\,在正则表达式字符串中则要写成\\)
3、原始值包装类型
每当用到某个原始值的方法或属性时,后台都会创建一个相应原始包装类型的对象,从而暴露出操作原始值的各种方法。
引用类型与原始值包装类型的主要区别在于对象的生命周期。在通过 new 实例化引用类型后,得到 的实例会在离开作用域时被销毁,而自动创建的原始值包装对象则只存在于访问它的那行代码执行期间。这意味着不能在运行时给原始值添加属性和方法。
Object 构造函数作为一个工厂方法,能够根据传入值的类型返回相应原始值包装类型的实例。
let obj = new Object("some text");
console.log(obj instanceof String); // true
复制代码
Boolean 的实例会重写 valueOf()
方法,返回一个原始值 true 或 false。toString()
方法被调用时也会被覆盖,返回字符串"true"或"false"。
Boolean(false).toString() // "false"
复制代码
Number类型重写了 valueOf()
方法返回 Number 对象表示的原始数值。toString()
方法可选地接收一个表示基数的参数,并返回相应基数形式的数值字符串
toFixed()
方法返回包含指定小数点位数的数值字符串
String 对象的 valueOf()
、toLocaleString()
和 toString()
都返回对象的原始字符串值。
使用 charCodeAt()
方法可以查看指定码元的字符编码。这个方法返回指定索引位置的码元值,索引以整数指定。fromCharCode()
方法用于根据给定的 UTF-16 码元创建字符串中的字符。这个方法可以接受任意多个数值,并返回将所有数值对应的字符拼接起来的字符串
let message = "abcde";
console.log(message.charCodeAt(2)); // 99
console.log(String.fromCharCode(0x61, 0x62, 0x63, 0x64, 0x65)); // "abcde"
复制代码
为正确解析既包含单码元字符又包含代理对字符的字符串,可以使用 codePointAt()
来代替 charCodeAt()
。
codePointAt()
接收 16 位码元的索引并返回该索引位置上的码点(code point)。码点是 Unicode 中一个字符的完整标识。比如,"c"的码点是 0x0063,而 "☺"的码点是 0x1F60A。
ECMAScript 在所有字符串上都提供了 repeat()
方法。这个方法接收一个整数参数,表示要将字符串复制多少
padStart()
和 padEnd()
方法会复制字符串,如果小于指定长度,则在相应一边填充字符,直至满足长度条件。这两个方法的第一个参数是长度,第二个参数是可选的填充字符串,默认为空格 (U+0020)。
let stringValue = "foo";
console.log(stringValue.padStart(6)); // " foo"
console.log(stringValue.padStart(9, ".")); // "......foo"
复制代码
4、单例内置对象
Global 对象是 ECMAScript 中最特别的对象,因为代码不会显式地访问它。ECMA-262 规定 Global 对象为一种兜底对象,它所针对的是不属于任何对象的属性和方法。事实上,不存在全局变量或全局函数这种东西。在全局作用域中定义的变量和函数都会变成 Global 对象的属性 。
1. URL 编码方法
encodeURI()
和 encodeURIComponent()
方法用于编码统一资源标识符(URI),以便传给浏览器。有效的 URI 不能包含某些字符,比如空格。
这两个方法的主要区别是,encodeURI()
不会编码属于 URL 组件的特殊字符,比如冒号、斜杠、问号、井号,而 encodeURIComponent()
会编码它发现的所有非标准字符。
与 encodeURI()
和 encodeURIComponent()
相对的是 decodeURI()
和 decodeURIComponent()
。
2、eval
let msg = "hello world";
eval("console.log(msg)"); // "hello world"
eval("function sayHi() { console.log('hi'); }");
sayHi();// hi
eval("let msg = 'hello world';");
console.log(msg); // Reference Error: msg is not defined
复制代码
eval()
会创建块作用域,所以不能访问到 msg
用 var
则没问题~,书上这里有错误~
当代码开始执行时,全局上下文中会存在两个内置对象:Global
和 Math
。Global
对象在大多数 ECMAScript 实现中无法直接访问。不过,浏览器将其实现为 window
对象。所有全局变量和函数都是 Global
对象的属性。