红宝书学习笔记【第6章】

红宝书学习笔记【第6章】

1. Object

1.1 创建 Object 实例

js 中显式的创建Object实例有两种方式.

  1. 通过new 和 Object构造函数
  2. 对象字面量
let obj1 = new Object();
obj1.name = "name";
obj1.age = 29;

let obj2 = {};
obj2.name = "name";
obj2.age = 29;

对象字面量表示发中, 属性名可以是字符串或数值. 但是数值属性会自动转换为字符串.

let person = {
  "name": "Nicholas", 
  "age": 29, 
  5: true
}

使用对象字面量表示法定义对象时, 不会实际调用 Object 构造函数.

1.2 访问对象的属性

访问对象中的属性有两种方式:

  1. 点语法
  2. 中括号

使用中括号方式时, 要在括号内使用属性名的字符串形式, 或使用值为属性名的变量.

console.log(person.name); // "Nicholas"
console.log(person["name"]); // "Nicholas" 
let a = "name"
console.log(person.[a])

中括号方式的优势是, 可以访问到不是正常变量名的属性.

let obj = {
  "first name": "Nicholas"
}

console.log(obj["first name"])

2. Array

2.1 创建数组

  1. 数组构造函数
let arr = new Array(); // 创建一个空数组
let colors = new Array(3); // 创建一个包含 3 个元素的数组, 数组长度为3, 每一项都是undefined.
let names = new Array("red", "blue", "green"); // 创建了一个包含"red", "blue", "green" 的数组

Array 构造函数可传入一个或多个参数.

当传入多个参数时, 会创建一个包含这几个参数的数组.
当传入一个参数时:
如果参数是数值, 表示创建长度为该数值的数组;
如果参数是字符串, 表示创建包含该字符串的数组.

这很容易产生歧义.

在使用Array构造函数时, 可以省略 new 操作符, 结果一样.

  1. 数组字面量方式
let colors = ["red", "blue", "green"]; // 创建一个包含 3 个元素的数组
let names = []; // 创建一个空数组
let values = [1,2,]; // 创建一个包含 2 个元素的数组

注意: 与对象一样,在使用数组字面量表示法创建数组不会调用 Array 构造函数。

  1. from() 和 of()

es6 引入了两个用于创建数组的静态方法: from()of().

from() 方法用于将类数组结构转换为数组实例.
第一个参数是一个类数组对象, 即任何刻碟带的结构, 或有一个length属性和可索引元素的结构.

// 字符串
Array.from("Matt"); // ["M", "a", "t", "t"]

// Map会转为entry数组
const m = new Map().set(1, 2) 
  .set(3, 4); 
const s = new Set().add(1) 
  .add(2) 
  .add(3) 
  .add(4);
console.log(Array.from(m)); // [[1, 2], [3, 4]] 
console.log(Array.from(s)); // [1, 2, 3, 4]

// Array.from()对现有数组执行浅复制
const a1 = [1, 2, 3, 4]; 
const a2 = Array.from(a1); 
console.log(a1); // [1, 2, 3, 4] 
alert(a1 === a2); // false

// from()转换带有必要属性的自定义对象
const arrayLikeObject = { 
 0: 1, 
 1: 2, 
 2: 3, 
 3: 4, 
 length: 4 
}; 
console.log(Array.from(arrayLikeObject)); // [1, 2, 3, 4]

from() 方法还接收第二个可选的映射函数参数, 第三个可选的指定映射函数this指向的参数.

const a1 = [1, 2, 3, 4]; 
const a2 = Array.from(a1, x => x**2);
const a3 = Array.from(a1, function(x) { return x**this.exponent }, { exponent: 2 }); 
console.log(a2); // [1, 4, 9, 16] 
console.log(a3); // [1, 4, 9, 16]

of() 方法用于将一组值转换为数组实例.

console.log(Array.of(1, 2, 3, 4)); // [1, 2, 3, 4] 
console.log(Array.of(undefined)); // [undefined]

2.2 数组空位

字面量方式创建数组时, 可以用过一串逗号来创建空位.

const options = [,,,,,]; // 创建包含 5 个元素的数组
console.log(options.length); // 5

ES6新增的方法和迭代器与早期ES版本中存在的方法行为不同, ES6会将这些空位当做undefined.

const options = [1,,,,5]; 
for (const option of options) { 
  console.log(option === undefined);
}
// false
// true
// true
// true
// false

ES6之前的方法会将空位处理为undefined.

console.log(options.join("-")); // "1---5" 
console.log(options[2]); // undefined 
console.log(options.map(n => n || "yes")); // [1, "yes", "yes", "yes", 5]

而ES6之前的方法会忽略这些空位.

const options = [1,,,,5];
console.log(options.map(() => 6)); // [6, undefined, undefined, undefined, 6]
console.log(options.join('-')); // "1----5"

2.3 数组索引

要取得或设置数组的值, 需要使用中括号并提供值的数字索引.
如果索引值小于数组包含的元素数量, 会返回存储在相位置的值.
如果把一个值设置给超出数组最大索引的索引, 数组长度会自动扩展.

let arr = [0, 1, 2, 3];
arr[4] = 4;
console.log(arr[4]); // 4
console.log(arr.length); // 5

数组的length不是只读的, 通过修改length的值, 可以从数组末尾删除或添加元素.

let arr = [0, 1, 2, 3];
arr.length = 2;
console.log(arr[2]); // undefined

let arr = [0, 1, 2]
arr.length = 4
console.log(arr[3]); // undefined

可以通过length属性项数组末尾添加元素.
因为数组最后一个元素的索引始终是length - 1, 因此下一个新增槽位的索引是length, 而每次在数组后面再新增一项后, 数组的length属性会更新.

let arr = [0, 1, 2];
arr[arr.length] = 3;

2.4 检测数组

在只有一个网页(全局作用域)的情况下, 可以通过value instanceof Array来判断一个对象是不是数组.

但如果网页理由多个框架, 可能涉及两个不同的全局执行上下文, 因此会有两个不同版本的Array构造函数. 如果把数组从一个框架传给另一个框架, 则这个数组的构造函数将有别于第二个床架内的Array构造函数.

未解决这个问题, 可以使用Array.isArray()方法.

2.5 迭代器方法

es6新增了三个用于检索数组内容的方法: keys(), values(), entries().

const a = ["foo", "bar", "baz", "qux"]; 
// 因为这些方法都返回迭代器,所以可以将它们的内容
// 通过 Array.from()直接转换为数组实例
const aKeys = Array.from(a.keys()); 
const aValues = Array.from(a.values()); 
const aEntries = Array.from(a.entries()); 
console.log(aKeys); // [0, 1, 2, 3] 
console.log(aValues); // ["foo", "bar", "baz", "qux"] 
console.log(aEntries); // [[0, "foo"], [1, "bar"], [2, "baz"], [3, "qux"]]

for (const [idx, element] of a.entries()) { 
  console.log(idx, element);
}

2.6 复制和填充

2.6.1 fill()

fill() 的第一个参数表示填充值, 第二和第三个参数表示索引的范围(左闭右开), 传入负数表示数组长度加上负值得到的正索引.
fill()会忽略超出数组边界, 零长度和方向相反的索引范围.

2.6.2 copyWithin()

该方法可以指定数组内的一部分复制到另一部分.

copyWithin() 第一个参数指粘贴的起始位置. 第二和第三个参数表示复制的起始位置(左闭右闭). 对负值参数的处理和fill()一样.
copyWithin()静默忽略超出数组边界、零长度及方向相反的索引范围

let ints = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

// 从 ints 中复制索引 0 开始的内容,插入到索引 5 开始的位置
// 在源索引或目标索引到达数组边界时停止
ints.copyWithin(5); 
console.log(ints); // [0, 1, 2, 3, 4, 0, 1, 2, 3, 4]

// reset()

// 从 ints 中复制索引 0 开始到索引 3 结束的内容
// 插入到索引 4 开始的位置
ints.copyWithin(4, 0, 3); 
alert(ints); // [0, 1, 2, 3, 0, 1, 2, 7, 8, 9]

2.7 转换方法

数组的valueOf()返回数组本身.

toString()方法会调用每个元素的toString()方法后用逗号拼接.
toLocaleString()方法会调用每个元素的toLocaleString()方法后用逗号拼接.

let colors = ["red", "green", "blue"]; 
console.log(colors.toString()); // red,green,blue 
console.log(colors.valueOf()); // [ 'red', 'green', 'blue' ]
console.log(colors); // [ 'red', 'green', 'blue' ]

join() 方法接收一个字符串作为分隔符, 将所有项拼接成一个字符串返回.

注意: 如果数组中存在undefinednull值, 该值在以上方法的返回值中以空字符串表示.

2.8 栈方法

对数组的尾部进行操作.

let arr = [0, 1, 2, 3];
arr.push(4); // [0, 1, 2, 3, 4]
arr.pop(); // [0, 1, 2, 3]

2.9 队列方法

对数组的头部进行操作.

let arr = [0, 1, 2, 3];
arr.unshift(4); // [4, 0, 1, 2, 3]
arr.shift(); // [0, 1, 2, 3]

2.10 排序方法

reverse()方法会反转数组中元素的顺序.

sort()方法默认情况下会在每一项上调用String()转型函数, 通过比较字符串, 按照升序排列数组元素.
还可以传入比较函数, 比较函数接受两个参数, 一个是当前项, 一个是下一项.
比较函数返回0, 表示两项相等; 返回正值, 表示当前值应该排在下一项之后; 返回负数, 表示当前值应该在下一项之前.

let arr = [0, 1, 2, 3];
arr.sort((a, b) => a - b); // [0, 1, 2, 3]

2.11 操作方法

2.11.1 concat()

调用该方法时, 会先创建当前数组的副本, 然后将他的参数添加在副本的尾部后返回.

let colors = ["red", "green", "blue"];
let colors2 = colors.concat("yellow", ["black", "brown"]);
console.log(colors); // ["red", "green","blue"] 
console.log(colors2); // ["red", "green", "blue", "yellow", "black", "brown"]

concat()会将传入的参数打平后再添加到副本数组中.
该行为可通过增加符号Symbol.isConcatSpreadablefalse来重写.

let colors = ["red", "green", "blue"]; 
let newColors = ["black", "brown"];
newColors[Symbol.isConcatSpreadable] = false;
let colors2 = colors.concat("yellow", newColors);
console.log(colors2); // ["red", "green", "blue", "yellow", ["black", "brown"]]
2.11.2 slice()

slice(start, end)方法返回一个新数组.
参数中的负值表示数组长度和负值的和.
start大于end时, 会返回空数组.

2.11.3 splice()

splice() 方法可传入多个参数. 常用来删除

第一个参数表示开始操作的下标.
第二个参数表示要删除的元素个数.
剩余的参数表示要从第一个参数指向的位置插入的值.

2.12 搜索方法

2.12.1 indexOf() lastIndexof() includes()

前两者在所有版本都可用, 后者是es7新增的.
前两者返回index, 没查到返回-1, 后者返回boolean值.
都可以接受两个参数, 第一个是要查找的元素, 第二个是开始查找的位置.
比较搜索值和数组中的元素时, 都采用全等运算.

2.12.2 find() findIndex()

两方法都接收一个断言函数, 每个索引都会调用这个函数, 断言函数返回真值表示匹配.

断言函数有三个参数, 分别是当前值、当前索引和数组本身.
一旦找到匹配项, 这两个方法都不会再继续查找.

const people = [ 
  { name: "Matt", age: 27 }, 
  { name: "Nicholas", age: 29 } 
];
console.log(people.find((element, index, array) => element.age < 28)); 
// { name: "Matt", age: 27 } 
console.log(people.findIndex((element, index, array) => element.age < 28)); 
// 0

2.13 迭代器方法

  • every()
  • filter()
  • forEach()
  • map()
  • some()

2.14 归并方法

reduce()方法从数组第一项开始遍历到最后一项. 而 reduceRight()从最后一项开始遍历至第一项

两者都接受一个归并函数和一个起始值.
归并函数有四个参数, 分别是: 上一个归并值, 当前项, 当前项的索引, 数组本身.

3. 定型数组(暂时用不到, 先不学了)

4. Map

4.1 基本API

4.1.1 构造函数

// 创建空映射
const m = new Map();

// 可通过传入entry数组来创建映射
const m1 = new Map([
 ["key1", "val1"],
 ["key2", "val2"],
 ["key3", "val3"]
]);
alert(m1.size); // 3

const testMap = new Map([[]]); // 传入空数组也会被成功创建
alert(testMap.has(undefined)); // true 
alert(testMap.get(undefined)); // undefined

// 使用自定义迭代器初始化映射
const m2 = new Map({ 
  [Symbol.iterator]: function*() {
    yield ["key1", "val1"];
    yield ["key2", "val2"];
    yield ["key3", "val3"];
  }
}); 
alert(m2.size); // 3

Map的键可以是js中的任何数据类型.

4.1.2 操作方法

  • set(key, value) 设置键值对, 可链式调用
  • get(key) 获取键对应的值
  • has(key) 判断映射中是否存在key
  • delete(key) 删除键值对
  • clear() 清空映射

对键的判断标准采用 SameValueZero.

4.2 顺序和迭代

Map实例会维护键值对的插入顺序, 可以根据插入顺序迭代元素.

映射示例可以提供一个迭代器, 能以插入顺序生成entry数组.

const m = new Map([
  ["key1", "val1"], 
  ["key2", "val2"], 
  ["key3", "val3"] 
]); 
alert(m.entries === m[Symbol.iterator]); // true

for (let pair of m.entries()) { 
 alert(pair); 
} 
// [key1,val1] 
// [key2,val2] 
// [key3,val3]

console.log([...m]); // [[key1,val1],[key2,val2],[key3,val3]]

m.forEach((val, key) => alert(`${key} -> ${val}`));
// key1 -> val1 
// key2 -> val2 
// key3 -> val3

keys()和 values()分别返回以插入顺序生成键和值的迭代器.

4.3 Object 和 Map 的差别

基本性质:

  • Object的键只能是数值、字符串或 Symbols, Map的键可以是 js 中的任何类型.
  • Map实例会维护键值对的插入顺序, 可以根据插入顺序迭代元素.

内存和性能:

  • 内存占用: 不同浏览器情况不同, 但给定固定大小的内存, Map大约可以比Object多存储50%的键值对.
  • 插入性能: Map插入性能更快.
  • 查找速度: 差距较小, 但如果只包含少量键值对或涉及大量查找操作, 由于浏览器对Object的优化, 使用Object更优.
  • 删除性能: Map的删除性能更好.

5. WeakMap

5.1 基本API

弱映射中的键只能是 Object 或 继承自 Object 的类型.

const key1 = {id: 1},
 key2 = {id: 2},
 key3 = {id: 3};
// 使用嵌套数组初始化弱映射
const wm1 = new WeakMap([
 [key1, "val1"],
 [key2, "val2"],
 [key3, "val3"]
]);
alert(wm1.get(key1)); // val1
alert(wm1.get(key2)); // val2
alert(wm1.get(key3)); // val3

// 初始化是全有或全无的操作
// 只要有一个键无效就会抛出错误,导致整个初始化失败
const wm2 = new WeakMap([
  [key1, "val1"],
  ["BADKEY", "val2"],
  [key3, "val3"]
]);
// TypeError: Invalid value used as WeakMap key
typeof wm2;
// ReferenceError: wm2 is not defined

5.2 弱键

弱映射的弱, 指映射对键不属于正式的引用. 也就是说, 键会被垃圾回收.

const wm = new WeakMap();
wm.set({}, "val");

上述例子中, set方法初始化了一个新对象, 并将它用作了一个字符串的键.
但由于没有指向这个对象的其他引用, 所以在这行代码执行结束后, 这个对象键会被当做垃圾回收.
然后, 这个键值对会从弱映射中消失, 成为空映射.
最后由于值也没有被引用, 值也成为垃圾回收的目标.

由于 WeakMap 中的键值对任何时候都可能被销毁, 所以没具备迭代其键值对的能力.

6. Set

6.1 基本API

Set 在很多方面像是加强的 Map.

// 创建空集合
const s = new Set();

// 通过数组初始化集合
const s1 = new Set(["val1", "val2", "val3"]);
alert(s1.size); // 3

// 使用自定义迭代器初始化集合
const s2 = new Set({ 
  [Symbol.iterator]: function*() {
    yield "val1";
    yield "val2";
    yield "val3";
  }
}); 
alert(s2.size); // 3

初始化之后,可以使用 add()增加值,使用 has()查询,通过 size 取得元素数量,以及使用 delete()
和 clear()删除元素.
Set 可以包含任何 JavaScript 数据类型作为值。集合也使用 SameValueZero 操作.
add()和 delete()操作是幂等的。delete()返回一个布尔值,表示集合中是否存在要删除的值:

const s = new Set(); 
s.add('foo'); 
alert(s.size); // 1 
s.add('foo'); 
alert(s.size); // 1 
// 集合里有这个值
alert(s.delete('foo')); // true 
// 集合里没有这个值
alert(s.delete('foo')); // false

6.2 顺序与迭代

Set 会维护值插入时的顺序.

const s = new Set(["val1", "val2", "val3"]);
alert(s.entries === s[Symbol.iterator]); // true

for (let pair of s.entries()) { 
 alert(pair); 
} 
// [val1,val1] 
// [val2,val2] 
// [val3,val3]

console.log([...s]); // ["val1","val2","val3"]

s.forEach((val, dupVal) => alert(`${val} -> ${dupVal}`)); 
// val1 -> val1 
// val2 -> val2 
// val3 -> val3

集合也有entries()方法, 它返回一个迭代器, 可以按照插入顺序返回entry, entry中key和value相同.

7. WeakSet

与WeakMap类似, 对值不属于正式的引用, 不会阻止垃圾回收.
不可迭代.

8. 扩展和迭代

下一章详细学

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值