红宝书学习笔记【第6章】
1. Object
1.1 创建 Object 实例
js 中显式的创建Object实例有两种方式.
- 通过new 和 Object构造函数
- 对象字面量
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 访问对象的属性
访问对象中的属性有两种方式:
- 点语法
- 中括号
使用中括号方式时, 要在括号内使用属性名的字符串形式, 或使用值为属性名的变量.
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 创建数组
- 数组构造函数
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 操作符, 结果一样.
- 数组字面量方式
let colors = ["red", "blue", "green"]; // 创建一个包含 3 个元素的数组
let names = []; // 创建一个空数组
let values = [1,2,]; // 创建一个包含 2 个元素的数组
注意: 与对象一样,在使用数组字面量表示法创建数组不会调用 Array 构造函数。
- 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() 方法接收一个字符串作为分隔符, 将所有项拼接成一个字符串返回.
注意: 如果数组中存在undefined和null值, 该值在以上方法的返回值中以空字符串表示.
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.isConcatSpreadable为false来重写.
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)判断映射中是否存在keydelete(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. 扩展和迭代
下一章详细学
1110

被折叠的 条评论
为什么被折叠?



