阅读《JavaScript高级程序设计(第4版)》第六章及视频讲解
Object
创建对象
let obj1 = new Object()
obj1.name = 'Jane'
let obj2 = {
name: 'Jane'
}
let obj3 = {}
obj3.name = 'Jane'
在使用对象字面量表示法定义对象时,并不会实际调用object构造函数。js引擎会把{}解析为对象。
在创建函数时,最好的方式是对必选参数使用命名参数,再通过一个对象字面量来封装多个可选参数
function fn (num, args){
if(typeof args.names === 'string'){ // typeof检验属性是否存在
...
}
return num + 2 // num 必选参数
}
fn(10, {name: 'Jane'})
通常情况下点语法是对象的首选属性存取方式。除非访问属性时必须使用变量(不合法属性名、关键字、变量等)。
数组
创建数组
let arr1 = new Array()
let arr2 = new Array(20) // 创建一个长度为20的数组
let arr3 = new Array['red', 'green']
let arr4 = Array() // 可以省略new
let arr5 = []
let arr6 = ['red', 'green']
与对象一样,在使用数组字面量表示法创建数组不会调用Array构造函数。JavaScript引擎会把[]解析为数组
ES6新增用于创建数组的静态方法from()和of()
from()
用于将类数组转换为数组实例。第一个参数是一个类数组对象。即任何可迭代的结构。或者有一个length属性和可索引元素的结构。
// 字符串会被拆分为字符串数组
Array.from('Jane') // ['J', 'a', 'n', 'e']
// 将映射和集合转换为一个新数组
const m = new Map().set(1,2).set(3,4)
const s = new Set().add(1).add(2).add(3).add(4)
Array.from(m) // [[1, 2], [3, 4]]
Array.from(s) // [1, 2, 3, 4]
// 对数组进行浅复制(一层)
const arr1 = [1, 2, 3, 4]
const arr2 = Array.from(arr1) // 进行了存储地址的复制。已经是两个数组了
console.log(arr1 === arr2) // false 修改值相互不影响
// 可以使用任何可迭代的对象
const iter = {
*[symbol.iterator](){
yield 1;
yield 2;
}
}
console.log(Array.from(iter)) // [1, 2]
// arguments 对象
function getArgArray (){
return Array.from(arguments)
}
console.log(getArgArray(1, 2, 3, 4)) // [1, 2, 3, 4]
// 带有length属性和可索引元素的结构
const arrayLikeObject = {
0:1
1:2
2:3
length: 3
}
console.log(Array.from(arrayLikeObject) // [1, 2, 3]
第二个参数是可选的映射函数。可以直接增强新函数本身。第三个可选参数。映射函数中的this(箭头函数不适用)
const arr1 = [1, 2, 3, 4]
// ** 指数操作符 相当于x的2次幂
const arr2 = Array.from(arr1, x => {return x**2 })
const arr3 = Array.from(arr1, function(x){
return x ** this.exponent
},
{ expoent: 3}
})
console.log(arr2) // [1, 4, 9, 16]
console.log(arr3) // [1, 8, 27, 64]
of()
用于将一组参数转换为数组实例。用于替代Array.prototype.slice.call(arguments)
console.log(Array.of(1, 2, 3, 4)) // [1, 2, 3, 4]
console.log(Array.of(undefined)) // [undefined]
数组空位
由于行为不一致和性能隐患。在实践中应该避免使用数组空位,如确实需要。则用显示的undefined值替代
[1,,,4] => [1, undefined, undefined, 4]
数组索引
取得或者设置数组的值。使用中括号并提供相应值得索引
const colour = ['red','yellow','green']
console.log(colour[0]) // 读
colour[1] = 'black' // 改
colout[3] = 'pink' // 加
length
数组中的length属性始终返回0或者大于0的值
通过数组length属性,可以从数组末尾修改或者添加元素
let colour = ['red','yellow','green']
colour.length = 2 // ['red','yellow']
colour.length = 3 // ['red','yellow',不存在的值,返回undefined]
colour[colour.length] = 'brown' // 添加颜色。长度为4
colour[6] = blue // ['red','yellow',不存在的值,brown,不存在的值,blue]
检查数组
// 方法一 只能在有一个全局上下文的时候使用,多框架不适用,会有多个Array构造函数。
value instanceof Array
// 方法二 原生提供
Array.isArray(value)
// 方法三
Object.prototype.toString.call([]) === "[object Array]"
迭代器方法
keys() 返回数组索引的迭代器
values() 返回数组元素的迭代器
entries() 返回索引/值对的迭代器
应为以上这些方法都返回迭代器,所以可以通过Array.from()直接转换为数组
const arr = ['aaa','bbb','ccc','ddd']
// 使用Array.from()转换为数组实例
const aKeys = Array.from(arr.keys())
const aValues = Array.from(arr.keys())
const aEntries = Array.from(arr.entries())
console.log(aKeys) // [0, 1, 2, 3]
console.log(aValues) // ['aaa','bbb','ccc','ddd']
console.log(aEntries) // [[0, 'aaa'],[1, 'bbb'], [2, 'ccc'],[3, 'ddd']]
//ES6的结构非常容易在循环中解析键/值对
for(const [index, item] of arr.entries){
console.log(index) // 0, 1, 2, 3
console.log(item) // 'aaa','bbb','ccc','ddd'
}
复制和填充方法
fill()
批量复制方法。向一个已有的数组中插入全部或者部分相同的值。包含开始索引,不包含结束索引。
如果索引是负数。则加上数组的长度得到一个正索引。
const zeroes = [0, 0, 0, 0, 0]
// 填充整个数组
zeroes.full(5) // [5, 5, 5, 5, 5]
zeroes.full(0)
// 填充索引大于等于3的数组
zeroes.full(6,3) // [0, 0, 0, 6, 6]
zeroes.full(0)
// 填充索引大于等于1小于3的数组
zeroes.full(7,1,3) // [0, 7, 7, 0, 0]
zeroes.full(0)
// 填充索引大于等于1小于4的数组
zeroes.full(8, -4, -1) // [0, 8, 8, 8, 0]
zeroes.full(0)
默认忽略超出边界,方向相反的索引范围
const zeroes = [0, 0, 0, 0, 0]
// 索引过低,忽略
zeroes.full(1,-10, -6) // [0, 0, 0, 0, 0]
// 索引过高, 忽略
zeroes.full(1,10,15) // [0, 0, 0, 0, 0]
// 索引反向, 忽略
zeroes.full(2,4,1) // [0, 0, 0, 0, 0]
// 索引部分可见
zeroes.full(4, 3, 10) // [0, 0, 0, 4, 4]
copyWithin()
填充辅助方法。按照自定范围浅复制数组中的部分,在将他们插入都指定索引开始的位置。包含开始索引,不包含结束索引。
如果索引是负数。则加上数组的长度得到一个正索引。
Js引擎在插值前会完整复制范围内的值,因此复制期间不存在重写的风险。
let ints
reset = () => ints = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
reset()
// 从ints中复制索引0开始的内容。插入到索引5开始的位置,到达数组边界时停止
ints.copyWithin(5) // [0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
reset()
// 从ints中复制索引5开始的内容。插入到索引0开始的位置,
ints.copyWithin(0, 5) // [5, 6, 7, 8, 9, 5, 6, 7, 8, 9]
reset()
// 从ints中复制索引0开始到索引3结束的内容。插入到索引4开始的位置,
ints.copyWithin(4, 0, 3) // [0, 1, 2, 3, 0, 1, 2, 7, 8, 9]
reset()
// 从ints中复制索引0开始到索引3结束的内容。插入到索引4开始的位置,
ints.copyWithin(-4, -7, -3) // [0, 1, 2, 3, 4, 5, 3, 4, 5, 6]
reset()
默认忽略超出边界,方向相反的索引范围
// 索引过低,忽略
ints.copyWithin(1,-15, -12)
// 索引过高, 忽略
ints.copyWithin(1,12,15)
// 索引反向, 忽略
ints.copyWithin(2,4,2)
// 索引部分可用
ints.copyWithin(4, 7, 15) // [0, 1, 2, 3, 7, 8, 9, 7, 8, 9]
转换方法
toString() 返回由数组每个值的等效字符串拼接而成的一个逗号分隔符的字符串。
valueOf() 返回数组本身。
join() 返回数组的值由分隔符拼接的字符串。默认逗号分隔符(与toString()相同)。
如果数组中某一项是null或undefined,则在toString、valueOf()、join()中以空字符串表示。
修改数组
push() 将参数添加到数组末尾。返回数组的新长度。
pop() 删除数组的最后一项。返回被删除的项
unshift() 将参数添加到数组开头。返回数组的新长度。
shift() 删除数组的第一项。返回被删除的项
排序方法
reverse() 将数组反向排序 [1, 2, 3] => [3, 2, 1]
sort() 默认按照升序排列数组,接受一个比较函数。比较函数有两个参数。如果第一个参数应该在第二个参数前面,就返回负值,如果第一个参数应该在第二个参数后面,就返回正值,如果两个参数相等,就返回0。
// 比较函数
function compare(value1, value2){
if(value < value2){
return -1
}else if (value1 > value2){
return 1
}else {
return 0
}
}
如果是数值或者返回数值的对象(Date),可以更简单
let arr = [1, 20, 3, 6, 36, 4]
// a-b 升序
arr.sort((a,b) => a-b) // [1, 3, 4, 6, 20, 36]
// b-a 降序
arr.sort((a,b) => b-a) // [36, 20, 6, 4, 3, 1]
操作方法
concat()
在现有数组的基础上创建一个新的数组,然后把参数添加到副本末尾。
打平数组参数行为可以重写。在参数数组上指定一个特殊的符号:Symbol.isConcatSpreadable 参数为false可以阻止打平数组。为true强制打平数组。
const arr = ['red']
const b = ['yellow', 'blue']
b[Symbol.isConcatSpreadable] = false
arr.concat(b) // ['red', ['yellow', 'blue']]
slice()
用于创建一个包含原有数组一个或多个元素的新数组。包含开始索引,不包含结束索引。
如果索引是负数。则加上数组的长度得到一个正索引。
const colour = ['red', 'yellow', 'blue']
colour.slice(1, 2) // ['yellow']
splice()
主要目的是在数组中插入元素。返回删除的元素,未删除返回空数组
const colour = ['red', 'yellow', 'blue']
// 删除 splice(要删除的第一个元素位置,删除个数)
colour.splice(0, 1) // ['yellow', 'blue']
// 插入 splice(开始的位置,0 删除个数,插入的元素)
colour.splice(1, 0, 'orange', 'black') // ['yellow', 'orange', 'black', 'blue']
// 替换 splice(开始的位置,删除个数,插入的元素)
colour.splice(2, 2, 'pink', 'green') // ['yellow', 'orange', 'pink', 'green']
搜索方法
严格相等:indexOf()、lastIndexOf() 、includes()ES7 方法参数:要查找的元素和一个可选的的起始位置。
-
indexOf() 从开始向后查找。返回元素在数组中的位置。
-
lastIndexOf() 从末尾向前查找。返回元素在数组中的位置。
-
includes() ES7 从开始向后查找。返回布尔值。表示是否最少包含一个指定元素。
断言函数:ECMAScript允许按照定义的断言函数搜索数组,每个索引都调用这个函数。断言函数的返回值决定了相应索引的元素是否被认为匹配
find()、findIndex() 从开始向后查找。第一个参数断言函数,第二个可选参数指定断言函数内部this值。找到匹配后,这两个元素都不在继续搜索。
find() 返回第一个匹配的元素。
findIndex() 返回第一个匹配元素的索引
const people = [
{name: 'Jane', age: 27},
{name: 'Sun', age: 29}
]
people.find((element, index, array) => element.age < 28) // {name: Jane, age: 27}
people.findIndex((element, index, array) => element.age > 28) // 1
迭代方法
5个迭代方法都接收两个参数。以每一项为参数的函数,以及可选的函数的this值。不会对空数组进行检测。函数的参数:当前项、当前项索引、数组本身。
every() 对数组的每一项都运行传入的函数。如果对每一项都返回true。则这个方法返回true。
filter() 对数组的每一项都运行传入的函数。函数返回true的项会组成数组后返回。
forEach() 对数组的每一项都运行传入的函数。没有返回值。
map() 对数组的每一项都运行传入的函数。返回由每次函数调用的结果构成的数组。
some() 对数组的每一项都运行传入的函数。如果有一项函数返回true,则这个方法返回true。
归并方法
reduce()、reduceRight() 迭代数组的所有项,并在此基础上构建一个最终返回值。
参数:对数组的每一项都运行的归并函数。以及可选的归并起点的初始值。归并函数的参数:上一个归并值、当前项、当前项索引,数组本身。
如果没有传递归并起点的初始值。默认数组第一个为起点值。归并函数从第二个元素开始。
-
reduce() 从开始向后遍历。
-
reduceRight() 从末尾向前遍历。
const value = [1, 3, 4, 6, 20, 9]
const sum = value.reduce((prev, cur, index, array) => prev + cur) // 43
定型数组
WebGL
是一个JavaScript API,开发者能够编写涉及复杂图形的应用程序。可在任何兼容的Web浏览器中渲染高性能的交互式3D和2D图形,而无需使用插件。该API可以在HTML5 canvas 元素中使用。
定型数组
为了解决WebGL的JavaScript数组与原生数组不匹配的性能问题。提供JavaScript接口的C语言风格浮点值数组。提高JavaScript与WebGL等原生库交换二进制数据的效率。
ArrayBuffer
对象用来表示通用的、固定长度的原始二进制数据缓冲区。你不能直接操作 ArrayBuffer 的内容,而是要通过类型数组对象或 DataView 对象来操作,它们会将缓冲区中的数据表示为特定的格式,并通过这些格式来读写缓冲区的内容。数组缓冲区无法调整大小。
new ArrayBuffer(length)
-
length 要创建的
ArrayBuffer的大小,单位为字节。 -
返回一个指定大小的
ArrayBuffer对象,其内容被初始化为 0。
DataView
视图是一个可以从 二进制ArrayBuffer 对象中读写多种数值类型的底层接口,使用它时,不用考虑不同平台的字节序问题。
Map
一种新的集合类型。真正的键/值对。
创建Map
const m = new Map()
const m1 = new Map([['key1','val1'],['key2','val2']]) // 传入可迭代对象,包含键/值对
const m2 = new Map([[]]) // 期待键/值对, undefined: undefined
属性方法
set() 添加键值对。返回该Map对象。可以多个连起来操作
get()返回键对应的值,如果不存在,则返回undefined。
has() 返回一个布尔值,表示Map实例是否包含键对应的值。
size 返回Map对象的键/值对的数量。
delete() 如果 Map 对象中存在该元素,则移除它并返回 true;否则如果该元素不存在则返回 false。
clear() 移除Map对象的所有键/值对 。
const m = new Map()
.set('key1','val1')
.set('key2','val2')
console.log(m.has('key1')) // true
console.log(m.get('key1')) // val1
console.log(m.size // 2
console.log(m.delete('key1')) // true
m.clear(); console.log(m) // Map(0){}
SameValueZero比较
基本上相当于严格相等(===)来检查Map键的匹配性。有两个特殊的值。
- +0和-0做key时都会被转换为0。都会读取key为0的值,有且只有一个0为key的键值对
- NaN作为key时,会被认为是同一个键,有且只有一个NaN为key的键值对
对象内部属性的修改并不影响它们作为Map的键和值的映射。
顺序和迭代
Map实例会维护键值对的插入顺序。因此客户已根据插入顺序执行迭代操作。
Symbol.iterator映射实例提供迭代器(Iterator),能插入顺序生成[key, value]形式的数组。
entries() 默认迭代器。返回插入顺序的键值对。能把映射转为数组
keys() 返回插入顺序的键
values() 返回插入顺序的值
const m = new Map([ ['key1','val1'],
['key2','val2']
])
for(let item of m[Symbol.iterator]){
console.log(item) // ['key1','val1'] // ['key2','val2']
}
for(let item of m.entries()){
console.log(item) // ['key1','val1'] // ['key2','val2']
}
console.log([...m]) // [['key1','val1'],['key2','val2']]
for(let item of m.keys()){
console.log(item) // key1 // key2
}
for(let item of m.values()){
console.log(item) // val1 // val2
}
如果不使用迭代器。使用回调方式。则可以使用forEach(),依次传入键值对参数的函数,以及可选的函数的this值
// 参数显示value 然后是key,和迭代器相反,是为了和数组的forEach一样。先value
m.forEach((val, key) => {console.log(`${key} -> ${val}`})
// key1 -> val1
// key2 -> val2
Map 和Object的区别
Obj
- key 只能使用数值、字符串或者符号
- 不会维护属性的顺序
Map
- key 可以是任何JavaScript数据类型
- 会按照插入的顺序进行迭代操作
内存占用:给定固定大小的内存情况下。Map大约可以比Object多存储50%的键值对
插入性能:插入Map一般会比Object快一点。大量插入数据Map性能更加
查找速度:差异不大。在少量键值对或者把Object当做数组的情况下(连续整数键)。Object更快
删除性能:Map的删除操作比插入和查找更快。Object的删除饱受诟病。
WeakMap
”弱映射“是一种全新的集合类型。”弱“描述的是JavaScript垃圾回收程序对待”弱映射“中键的方式。
创建WeakMap
弱映射的键只能是Object或者继承自Object的类型,尝试使用非对象设置键会抛出TypeError。值没有限制。
const key1 = {id: 1}
const wm = new WeakMap([[key1, 'val1']])
初始化时全有或者全无的操作,只要有一个键是无效的,就会抛出错误,导致整个初始化失败。
原始值可以先包装成对象再用作键
const stringVal = new String('stringVal')
方法
set() 添加键值对。返回该WeakMap对象。可以多个连起来操作
get()返回键对应的值,如果不存在,则返回undefined。
has() 返回一个布尔值,表示WeakMap实例是否包含键对应的值。
delete() 如果 WeakMap 对象中存在该元素,则移除它并返回 true;否则如果该元素不存在则返回 false。
弱映射的键
弱键表示”弱弱的拿着“,不属于正式的引用。不会阻止垃圾回收。
在弱映射之外如果没有对象键的引用了,则这个对象键就会被回收
const wm = new WeakMap()
wm.set({}, 'value')
// set 初始化了一个新对象作为字符串的键。因为在WeakMap之外没有其他指向这个对象的引用了。所以当这行代码执行完毕后,这个对象键就会被当做垃圾回收。
在弱映射之外如果有维护弱映射键的引用,则这个对象键就会不会回收
const wm = new WeakMap()
const container = {data: {}}
wm.set(container.data, 'value')
// container 对象维护者一个对弱映射键的引用。因此这个对象键不会成为垃圾回收的目标。
function remove() {
container.data = null
}
// 如果调用了remove(),就会摧毁最后一个对象键的引用,垃圾回收就会清理这个对象键。
不可迭代键
因为WeakMap的键随时都可能被销毁。所以没有提供迭代键值对的方法。所以不能再不知道对象键的情况下取得WeakMap的值,即时代码能访问WeakMap实例,也没有办法看见其中的内容。
之所以键是对象。是因为原始值没有办法区分初始化的值和之后创建的值。对象可以区分。
使用弱映射
-
私有变量
-
保存关联元素数据
Set
一种新的集合类型。不会重复存储数据。
创建Set
const m = new Set()
const m1 = new Set(['val1','val2']]) // 传入可迭代对象
属性方法
add() 添加元素。返回该Set对象。可以多个连起来操作。多次添加同一个元素只添加一次。
has() 返回一个布尔值,表示Set实例是否包含对应的元素。
size 返回Set对象的元素的数量。
delete() 如果 Set 对象中存在该元素,则移除它并返回 true;否则如果该元素不存在则返回 false。
clear() 移除Set对象的所有元素 。
const s = new Set()
.add('val1').add('val2')
console.log(s.has('val1')) // true
console.log(s.size) // 2
console.log(s.delete('val1')) // true
s.clear(); console.log(s) // Set(0){}
SameValueZero比较
顺序和迭代
Set实例会维护键值对的插入顺序。因此客户已根据插入顺序执行迭代操作。
Symbol.iterator映射实例提供迭代器(Iterator),能插入顺序生成集合内容。
entries() 默认迭代器。返回插入顺序的两个元素的数组,这两个元素是值得重复出现。
keys() 返回插入顺序的值。
values() 默认迭代器。 返回插入顺序的值
const m = new Set(['val1','val2'])
for(let item of m[Symbol.iterator]){
console.log(item) // val1 // val2
}
for(let item of m.value()){
console.log(item) // val1 // val2
}
console.log([...m]) // ['val1','val2']
for(let item of m.keys()){
console.log(item) // val1 // val2
}
for(let item of m.entries()){
console.log(item) // ['val1', 'val1'] // ['val2', 'val2']
}
如果不使用迭代器。使用回调方式。则可以使用forEach(),依次传入键值对参数的函数,以及可选的函数的this值
// 参数显示value 然后是key,和迭代器相反,是为了和数组的forEach一样。先value
m.forEach((val, dupVal) => {console.log(`${val} -> ${dupVal}`})
// val1 -> val1
// val2 -> val2
WeakSet
”弱集合“是一种全新的集合类型。”弱“描述的是JavaScript垃圾回收程序对待”弱集合“中键的方式。
创建WeakSet
”弱集合“的值只能是Object或者继承自Object的类型,尝试使用非对象设置键会抛出TypeError。
const val1 = {id: 1}
const val2 = {id: 2}
const ws = new WeakSet([val1, val2])
初始化时全有或者全无的操作,只要有一个键是无效的,就会抛出错误,导致整个初始化失败。
原始值可以先包装成对象再用作值
const stringVal = new String('stringVal')
方法
add() 添加元素。返回该WeakSet对象。可以多个连起来操作。多次添加同一个元素只添加一次。
has() 返回一个布尔值,表示WeakSet实例是否包含对应的元素。
delete() 如果 WeakSet 对象中存在该元素,则移除它并返回 true;否则如果该元素不存在则返回 false。
弱集合的值
弱值表示”弱弱的拿着“,不属于正式的引用。不会阻止垃圾回收。
在WeakSet之外如果没有对象值的引用了,则这个对象值就会被回收
const ws = new WeakSet()
ws.add({})
// set 初始化了一个新对象作为值。因为在WeakSet之外没有其他指向这个对象的引用了。所以当这行代码执行完毕后,这个对象值就会被当做垃圾回收。
在WeakSet之外如果有维护WeakSet值的引用,则这个对象值就会不会回收
const ws = new WeakSet()
const container = {data: {}}
ws.add(container.data)
// container 对象维护者一个对WeakSet值的引用。因此这个对象值不会成为垃圾回收的目标。
function remove() {
container.data = null
}
// 如果调用了remove(),就会摧毁最后一个对象值的引用,垃圾回收就会清理这个对象值。
不可迭代键
因为WeakSet的键随时都可能被销毁。所以没有提供迭代的方法。所以不能再不知道对象引用的情况下取得WeakSet的值,即时代码能访问WeakSet实例,也没有办法看见其中的内容。
之所以值是对象。是因为原始值没有办法区分初始化的值和之后创建的值。对象可以区分。
使用弱映射
- 保存关联元素数据
迭代和扩展操作
ECMAScript6新增的迭代器扩展操作符对集合引用类型特别有用。这些新特性让集合类型之间相互操作、复制、和修改变得异常方便。
有4种原生集合类型定义了默认迭代器。
- Array
- 所有定型数组
- Map
- Set
这些类型都支持顺序迭代。都可以传入for-of。也都兼容扩展操作符(...)。扩展操作符对可以迭代的对象执行浅复制时特别有用。对弈可迭代的对象的构造函数只要传入可迭代的对象就可以实现复制。这些类型都支持多种构建方式,Array.of() 和 Array.from()
let arr = [1, 2, 3, 4, 4]
let arr2 = [0, ...arr]
const map = new Map([['key', arr], [1, 2]]) // Map(2) {"key" => Array(5), 1 => 2}
const set = new Set(arr) // Set(4) {1, 2, 3, 4}
let typeArr = Int16Array.of(...arr) // Int16Array(5) [1, 2, 3, 4, 4]
let arr 3 = Array.from(m) // [['key', Array(5)], [1, 2]]