重学JavaScript的第2天

160 阅读7分钟

Ch5 基本引用类型

5.1 Date

创建对象日期:let now = new Date();

在不给 Date 构造函数传参数的情况下,创建的对象将保存当前日期和时间。

1.png

要基于其他日期和时 间创建日期对象,必须传入其毫秒表示(UNIX 纪元 1970 年 1 月 1 日午夜之后的毫秒数)。

2.png

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

3.png

5.1.2 日期格式化方法

Date 类型有几个专门用于格式化日期的方法,它们都会返回字符串:

  • toDateString()显示日期中的周几、月、日、年(格式特定于实现);
  • toTimeString()显示日期中的时、分、秒和时区(格式特定于实现);
  • toLocaleDateString()显示日期中的周几、月、日、年(格式特定于实现和地区);
  • toLocaleTimeString()显示日期中的时、分、秒(格式特定于实现和地区);
  • toUTCString()显示完整的 UTC 日期(格式特定于实现)

4.png

5.2 RegExp

正则表达式格式:let expression = /pattern/flags;

或者是:let exp = new RegExp(pattern, flags)

  • pattern可以是任何正则表达式,包括字符类、限定符、 分组、向前查找和反向引用。
  • 每个正则表达式可以带零个或多个 flags(标记),用于控制正则表达式的行为。常见的flag:

    • g:全局匹配
    • i:不区分大小写

元字符:www.runoob.com/regexp/rege…

5.2.2 RegExp实例方法

主要方法是exec(),该方法接受一个参数,是一个目标字符串。如果找到匹配项,则返回包含第一个匹配信息的数组;如果没找到,则返回null,虽然返回的是Array的实例,但是包含index和input这两个额外属性,index是字符串匹配项的起始位置,input是要目标字符串。

值得注意的是:如果在这个模式上设置了 g 标记,则每次调用 exec()都会在字符串中向前搜索下一个匹配项

let text = "cat, bat, sat, fat"; 
let pattern = /.at/g; 
let matches = pattern.exec(text); 
console.log(matches.index);         // 0 
console.log(matches[0]);            // cat 
console.log(pattern.lastIndex);     // 3 
matches = pattern.exec(text); 
console.log(matches.index);         // 5 
console.log(matches[0]);            // bat 
console.log(pattern.lastIndex);     // 8 
matches = pattern.exec(text); 
console.log(matches.index);         // 10 
console.log(matches[0]);            // sat 
console.log(pattern.lastIndex);     // 13

5.3 原始包装类

为了方便操作原始值,ECMAScript 提供了 3 种特殊的引用类型:Boolean、Number 和 String。

为什么需要包装类?来看一个例子:

1: let s1 = "some text";
2: let s2 = s1.substring(2);        // "me text"

我们知道值类型并不是对象本身,实际上s1的属性上是没有substring方法的。第二行访问s1的时候,实际上会执行以下3步:

  • 创建一个String类型的实例
  • 调用实例上的方法
  • 销毁实例

可以把这三行想象成下面的代码:

let s1 = new String('some text');
let s2 = s1.substring(2);
s1 = null;

这种行为可以让原始值拥有对象的行为。对布尔值和数值而言,以上 3 步也会发生。

5.4 单例内置对象

5.4.1 Global

Global对象:是 ECMAScript 中最特别的对象,因为代码不会显式地访问它。ECMA-262 规定 Global 对象为一种兜底对象,它所针对的是不属于任何对象的属性和方法。事实上,不存在全局变量或全局函数这种东西。在全局作用域中定义的变量和函数都会变成 Global 对象的属性 。isNaN()、isFinite()、parseInt()和 parseFloat()等实际上都是 Global 对象的方法。

window 对象:虽然 ECMA-262 没有规定直接访问 Global 对象的方式,但浏览器将 window 对象实现为 Global 对象的代理。因此,所有全局作用域中声明的变量和函数都变成了 window 的属性。

5.4.2 Math

Math对象:ECMAScript 提供了 Math 对象作为保存数学公式、信息和计算的地方。Math 对象提供了一些辅助计算的属性和方法。

Ch6 集合引用类型

6.1 Object

Object类型可以说是JS中的根类型了。创建Object类型变量主要有两种:

  • 通过new和Object构造函数
  • 对象字面量
// 构造函数
let person = new Object(); 
person.name = "Nicholas"; 
person.age = 29;
​
// 对象字面量
let person = { 
 name: "Nicholas", 
 age: 29 
};

6.2 Array

ECMAScript 数组也是一组有序的数据,但跟其他语言不同的是,数组中每个槽位可以存储任意类型的数据。这意味着可以创建一个数组,它的第一个元素 是字符串,第二个元素是数值,第三个是对象。ECMAScript 数组也是动态大小的,会随着数据添加而 自动增长。

6.4 Map

Map 是一种新的集合类型,为这门语言带来了真正的键/值存储机制。Map 的大多数特性都可以通过 Object 类型实现,但二者之间还是存在一些细微的差异。

6.4.1 基本api

通过new关键字来实例化Map,参数可以接受一个数组,该数组的成员是一个个表示键值对的数组。

const item = [
  ['name', "gar"],
  ['school', "hust"]
]
​
const m1 = new Map(item)
console.log(m1);    // Map(2) { 'name' => 'gar', 'school' => 'hust' }

我们也可以通过set来给一个Map添加/修改键的值。

const m2 = new Map();
m2.set('name', 'gar');
m2.set('school', 'hust');
console.log(m2);    // Map(2) { 'name' => 'gar', 'school' => 'hust' }

我们通过get方法来获取键所对应的值。

const item = [
  ['name', "gar"],
  ['school', "hust"]
]
const m3 = new Map(item);
console.log(m3.get('name'));    // gar
console.log(m3.get('school'));  // hust

需要注意的是:Map中的键实际上都是对一个地址的引用,说白了就是指针,只有指针指向同一个地方,就是内存地址一样的情况下,Map才将其视为同一个键。下面通过一个例子来说明。

const m4 = new Map();
const a = [1,2];
const b = [1,2];
m4.set(['a'], 'aaa');
console.log(m4.get(['a']));   // undefined
m4.set(a,'array1');
m4.set(b,'array2');
console.log(m4.get(a));       // array1
console.log(m4.get(b));       // array2

上面代码中,当我们给Map结构设置一个键为['a']之后,通过get(['a'])来获取值但是最终获取到的是undefined,原因是在Map眼中,它们的内存地址不同,同理上面中的变量ab的值实际上是一样的,但是Map将他们视作了两个不同的键,因为他们俩在内存中的地址不同。

与 Object 只能使用数值、字符串或符号作为键不同,Map 可以使用任何 JavaScript 数据类型作为键。

6.4.2 顺序与遍历

与 Object 类型的一个主要差异是,Map 实例会维护键值对的插入顺序,因此可以根据插入顺序执行迭代操作。 映射实例可以提供一个迭代器,能以插入顺序生成[key, value]形式的数组。可以通过 entries()方法取得这个迭代器。

遍历Map的几种方法:

const item = [
  ['name', "gar"],
  ['school', "hust"]
]
​
let m = new Map(item);
​
for (const key of m.keys()) {
  console.log(key);
}
// name
// school
for (const value of m.values()) {
  console.log(value);
}
// gar
// hust
for (const item of m.entries()) {
  console.log(item[0], item[1]);
}
// name gar
// school hust
for (const item of m) {
  console.log(item[0], item[1]);
}
// name gar
// school hust
m.forEach((value, key)=>console.log(value, key))
// gar name
// hust school

6.4.3 Map Or Object?

对于多数 Web 开发任务来说,选择 Object 还是 Map 只是个人偏好问题,影响不大。不过,对于 在乎内存和性能的开发者来说,对象和映射之间确实存在显著的差别。

内存占用:给定固定大小的内存,Map 大约可以比 Object 多存储 50%的键/值对。

插入性能:Map 在所有浏览器中一般会稍微快一点儿。

查找速度:代码涉及大量查找操作,那么可能选择 Object 更好一些。

删除性能:Map 的 delete()操作优于Object。如果代码涉及大量删除操作,那么毫无疑问应该选择 Map。

6.5 WeakMap

可以使用 new 关键字实例化一个空的 WeakMap: const wm = new WeakMap();

弱映射中的键只能是 Object 或者继承自 Object 的类型,尝试使用非对象设置键会抛出 TypeError。值的类型没有限制。

6.5.2 弱键

WeakMap 中“weak”表示弱映射的键是“弱弱地拿着”的。意思就是,这些键不属于正式的引用, 不会阻止垃圾回收。

const wm = new WeakMap(); 
const container = { 
 key: {} 
}; 
wm.set(container.key, "val"); 
function removeReference() { 
 container.key = null; 
} 

以上代码,container 对象维护着一个对弱映射键的引用,因此这个对象键不会成为垃圾回收的目 标。不过,如果调用了 removeReference(),就会摧毁键对象的最后一个引用,垃圾回收程序就可以 把这个键/值对清理掉。

用途:保存DOM节点元数据、私有变量