JS学习笔记(五)集合引用类型
文章目录
一、Object
1.1 Object创建实例的方法
1. 使用new(用的少)
let person = new Object();
let person = {};//与new Object();相同
person.age = 29;
2. 使用对象字面量表示法(更好)
let person = {
name :"niki";
age:29;
}
3. 利用构造函数
构造函数就是吧对象里一些相同的属性和方法抽象出来封装到函数里面
构造函数的语法格式
funcion 构造函数名(){
this.属性 = 值;
this.方法 = function() {}
}
new 构造函数名();
function Star(uname,age,sex) {
this.name = uname;
this.age = age;
this.sex = sex;
}
var zjl = new Star('周杰伦',18,'男');
注意:
- 构造函数名字首字母要大写
- 构造函数不需要return就可以返回结果
- 调用构造函数必须要用new
- 属性和方法前面必须添加this
1.2 存取属性的方法
- 点语法
console.log(person.name);
- 使用中括号
console.log(person["name"]);
注意:属性名是字符串,对象名[‘属性名’]
1.3 for in 遍历对象属性
var obj = {
name: 'niki',
age: 18,
sex: '女',
foo: function () {}
}
for(var k in obj) {
console.log(k); //输出得到属性名
console.log(obj[k]); //输出属性值
}
二、Array
ES数组与其他语言的数组不一样在于:
- ES数组中每个槽位可以存储任意类型的数据
- ES数组是动态大小,会随着数据添加而自动增长
2.1 创建数组
1. 使用Array构造函数
let colors = new Array();
2. 使用数组字面量
let color = ["red","green","black"];
let names = [];
2.2 创建数组的静态方法
2.2.1. from()
用于将类数组转换为数组实例,第一个参数为类数组对象,即任何可迭代的结构,或者有一个length属性和可索引元素的结构
//字符串被拆分为单字符数组
console.log(Array.from('niki')); //["n","i","k","i"]
//可以使用from()将集合和映射转换为一个数组
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);
alert(a1==a2); //false
//迭代对象
const arrayLikeObj = {
0:1,
1:2,
2:3,
3:4,
length:4
};
console.log(Array.from(arrayLikeObj)); // [1,2,3,4]
Array.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(Array.from(a2)); //[1,4,9,16]
console.log(Array.from(a3)); //[1,4,9,16]
2.2.2. of()
用于将一组参数转换为数组实例
console.log(Array.of(1,2,3,4)); //[1,2,3,4]
console.log(Array.of(undefined)); //[undefined]
2.3 数组空位
使用数组字面量初始化时,可以使用一串逗号来创建空位。ES会将逗号之间相应索引位置的值当成空值
const options = [,,,,,];
console.log(options.length); //5
console.log(Array.of(...[,,,])); //[undefined,undefined,undefined]
2.4 检测是否为数组
2.4.1. instanceof
假定只有一个全局上下文
var arr = [];
console.log(arr instanceof Array);
2.4.2.Array.isArray(参数)(更好)
console.log(Array.isArray(arr));
2.5 迭代器方法
Array原型上有3个用于检索数组内容的方法:keys()、values()和 entries()
- keys():返回数组索引的迭代器
- values():返回数组元素的迭代器
- entries():返回索引/值对的迭代器
const a = ["foo","bar","baz","hhh"];
const Keys = Array.from(a.keys());
const Values = Array.from(a.values());
const Entries = Array.from(a.entries());
console.log(Keys); //[0,1,2,3]
console.log(Values); //["foo","bar","baz","hhh"]
console.log(Entries);//[[0,"foo"],[1,"bar"],[2,"baz"],[3,"hhh"]]
//ES6的解构在循环中拆分键值对
const a = ["foo","bar","baz","hhh"];
for (const [idx,element] of a.entries()) {
console.log(idx);
console.log(element);
}
// 0
// foo
// 1
// bar
// 2
// baz
// 3
// hhh
2.6 复制和填充方法
2.6.1.copyWithin()
批量复制,不会改变数组的大小,按照指定范围浅复制数组中的部分内容,然后将他们插入到指定索引开始的位置
语法:
被复制的对象.copyWithin(内容插入的位置,开始复制的索引位置,结束复制的索引位置);
let ints,
reset = () => ints = [0,1,2,3,4,5,6,7,8,9];
reset();
//从ints中复制索引0开始的内容,插入到索引5开始的位置
ints.copyWithin(5);
console.log(ints); //[0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
2.6.2. fill()
向一个数组中插入全部或部分相同的值
const zeroes = [0,0,0,0,0];
zeroes.fill(8,-4,-1); //[0,8,8,8,0]
2.7 添加删除数组元素(栈方法和队列方法)
方法名 | 说明 | 返回值 |
---|---|---|
push(参数1…) | 栈方法,接收任意数量的参数,并将它们添加到数组末尾,修改了原数组 | 返回数组的最新长度 |
pop() | 栈方法,删除数组的最后一个元素,把数组长度减一,修改原数组 | 返回删除的元素 |
unshift(参数1…) | 队列方法,向数组的开头添加任意多个值,修改原数组 | 返回数组的最新长度 |
shift() | 队列方法,删除数组的第一个元素,修改原数组 | 返回第一个元素 |
2.8 新增元素
1. 通过length长度新增数组元素
var arr = ["red","green","blue","pink"];
arr.length = 7;
console.log(arr[5]); //undefined
2. 通过修改数组索引新增数组元素
- 不能直接给数组名复制,否则会覆盖掉以前的数据
var arr = ["red","green","blue","pink"];
arr[4] = 'hotpink';
console.log(arr);
2.9 排序方法
2.9.1 reverse()
原数组改变
let val = [1,2,3,4,5];
val.reverse();
console.log(val)
2.9.2 sort()
原数组改变,可以接受一个比较函数,用于判断哪个值在前面
- 若val1和val2的大小关系位置正确,则返回负值
- 若val1和val2的大小关系位置错误,需要交换,则返回正值
// 逆序排序
function compare(value1, value2) {
if (value1 > value2) {
return -1;
} else if (value1 < value2) {
return 1;
} else {
return 0;
}
}
let values = [0, 1, 5, 10, 15];
values.sort(compare);
console.log(values); //[15,10,5,1,0]
// 更简单的排序(冒泡)
let arr = [0, 1, 5, 10, 15];
arr.sort(function(a,b) {
// return a - b; 升序的顺序排列
return b - a; //降序
})
console.log(arr);
2.10 操作方法
2.10.1 concat()
创建一个当前数组的副本,然后把他的参数添加到末尾,最后返回一个新构建的数组。若传入一个或多个数组,则concat()会把数组的每一项都添加进结果数组。若参数不是数组,则把它直接添加到结果数组末尾。
let colors = ["red", "green", "blue"];
let color2 = colors.concat("yellow", ["black", "brown"]);
console.log(color2); //['red', 'green', 'blue', 'yellow', 'black', 'brown']
打平数组的行为可以重写,方法是在参数数组上指定一个特殊的符号:Symbol.isConcatSpreadable。
let colors = ["red", "green", "blue"];
let newColors = ["black", "brown"];
let moreNewColors = {
[Symbol.isConcatSpreadable]: true,
length: 2,
0: "pink",
1: "cyan"
};
newColors[Symbol.isConcatSpreadable] = false;
// 强制不打平数组
let color3 = colors.concat("yellow", newColors);
// 强制打平类数组对象
let color4 = colors.concat(moreNewColors);
console.log(color3); //['red', 'green', 'blue', 'yellow', ["black", "brown"]]
console.log(color4);// ['red', 'green', 'blue', 'pink', 'cyan']
2.10.2 slice()
用于创建一个包含原有数组中一个或者多个元素的新数组。
语法:slice(返回元素的开始索引,结束索引)。不包含结束索引对应的元素。不影响原数组
2.10.3 splice()
返回从数组中删除的元素的数组
- 删除。传两个参数:要删除的第一个元素的位置和要删除的元素数量
- 插入。传三个参数:开始位置、0(要删除的元素的个数)和要插入的元素(可多个)
- **替换。**传三个参数:开始位置、要删除的元素的个数(不为0)和要插入的任意多个元素
let colors = ["red", "green", "blue"];
let removed =colors.splice(0,1); //删除第一项
console.log(colors); //['green', 'blue']
removed =colors.splice(1,0,"yellow","orange");
console.log(colors); //['green', 'yellow', 'orange', 'blue']
console.log(removed); //[]
removed =colors.splice(1,1,"red","purple");
console.log(colors); //['green', 'red', 'purple', 'orange', 'blue']
2.11 搜索和位置方法
两类搜索数组的方法:按严格相等搜索和按断言函数搜索
2.11.1 严格相等
ES提供3个严格相等的搜索方法:indexOf()
、lastIndexOf()
、includes()
案例:数组去重
目标:把旧数组里面不重复的元素选取处理啊放到新数组中,重复的元素只保留一个,放到新数组中
核心算法:遍历旧数组,拿旧元素去查询新数组,若该元素在新数组里面没有出现过,就添加,否则不添加
利用新数组.indexOf(数组元素),若返回-1则说明新数组里面没有该元素
function unique(){
var newArr =[];
for (var i =0;i<arr.length;i++) {
if(newArr.indexOf(arr[i] === -1)) {
newArr.push(arr[i]);
}
}
return newArr;
}
2.11.2 断言函数
断言函数接收3个参数:
- 元素:数组中当前搜索的元素
- 索引:当前元素的索引
- 数组本身
断言函数返回真值,表示是否匹配
1. find()
从数组的最小索引开始,返回第一个匹配的元素
const people = [
{
name: "Elvira",
age: 20
},
{
name: "Niki",
age: 80
}
];
console.log(people.find((element, index, array) => element.age < 28));
// {name: "Elvira", age: 20}
2. findIndex()
从数组的最小索引开始,返回第一个匹配的元素的索引
const people = [
{
name: "Elvira",
age: 20
},
{
name: "Niki",
age: 80
}
];
console.log(people.findIndex((element, index, array) => element.age < 28));
// 0
2.12 迭代方法
ES为数组定义了5个迭代方法:每个方法接收两个参数。传给每个方法的函数接收3个参数:数组元素、元素索引和数组本身,对数组的每一项都运行传入的函数
- every(): 如果对每一项都返回true,则此方法返回true
- some(): 只要有一项返回true,则该方法返回true
- filter(): 返回true的项组成数组后返回
- forEach(): 无返回值,相当于使用for循环遍历数组
- map(): 返回由每次函数调用的结果构成的数组
语法:
数组名.方法名((item,index,array) => 操作);
2.13 归并方法
- reduce(): 从第一项开始遍历到最后一项
- reduceRight(): 从最后一项开始遍历到第一项
俩个方法都接受两个参数:对每一项都会运行的归并函数以及可选的归并起点的初始值
传给方法的函数接收4个参数:上一个归并值、当前项、当前项的索引和数组本身。若没有传入可选的第二个参数(作为归并值的起点),则第一次迭代将从第二项开始。
let values = [1, 2, 3, 4, 5];
let sum = values.reduce((prev, cur, index, array) => prev + cur);
console.log(sum);
三、定型数组
3.1 ArrayBuffer
- ArrayBuffer 是所有定型数组及视图引用的基本单位。
- 是一个构造函数,可用于在内存中分配特定数量的字节空间。
- ArrayBuffer 一经创建就不能再调整大小
3.2 DataView
- 允许读写ArrayBuffer的视图。DataView对缓冲内容没有任何预设,也不能迭代。
- 必须在对已有的ArrayBuffer读取或写入时才能创建DataView实例。该实例可以使用部分或者全部ArrayBuffer,且维护着对该缓冲实例的引用以及试图在缓冲中开始的位置
const buf = new ArrayBuffer(16);
const firstDataView = new DataView(buf, 0, 8);
console.log(firstDataView.byteOffset); //0 字节偏移量,从缓冲起点开始
console.log(firstDataView.byteLength); //8
console.log(firstDataView.buffer === buf); //true
通过DataView读取缓冲,需要的组件
- 读或写的字节偏移量
- DataView应该使用ElementType来实现JavaScript得到Number类型到缓冲内二进制格式的转换,每种类型都有get和set方法
- 内存中值的字节序。默认为大端字节序
1. 字节序
大端字序节也称网络字序节:最高有效位保存在第一个字节,最低有效位保存在最后一个字节
const buf = new ArrayBuffer(2);
const view = new DataView(buf);
view.setUint8(0,0x80); //最左边的位等于1
view.setUint8(1,0x01); //最右边的位等于1
//缓冲内容
//0x8 0x0 0x0 0x1
//1000 0000 0000 0001
//0x8001 = 2^15+2^0 =32769
console.log(view.getUint16(0)); //32769
// 0x01是高字节,0x80是低字节
//0x0180 = 2^8+2^7=384
console.log(view.getUint16(0,true)); //384
//缓冲内容
//0x0 0x0 0x0 0x4
//0000 0000 0000 0100
view.setUint16(0,0x0004);
console.log(view.getUint8(0)); //0
console.log(view.getUint8(1)); //4
//缓冲内容
//0x0 0x2 0x0 0x0
//0000 0010 0000 0000
view.setUint16(0,0x0002,true);
console.log(view.getUint8(0));//2
console.log(view.getUint8(1));//0
3.3 定型数组
是另一种形式的ArrayBuffer视图,特定与一种ElementType且遵循系统原生的字节序。目的是:提高与WebGL等原生库交换二进制数据的效率。
创建定型数组的方式:
- 读取已有的缓存
- 使用自有缓存
- 填充可迭代结构
- 填充基于任意类型的定型数组
<ElementType>.from()
和<ElementType>.of()
const buf = new ArrayBuffer(12);
const ints = new Int32Array(buf);
console.log(ints.length); //3,每个元素需要4个字节
const ints1 =new Int16Array([2,4,6,8]);
console.log(ints1.length); //4
console.log(ints1.buffer.byteLength); //8=4*2(16位=2个字节)
console.log(ints1[2]);//6
3.3.1 合并、复制和修改定型数组
定型数组同样使用数组缓冲来存储数据,而数组缓冲无法调整大小。因此下列方法不适用于定型数组:
- concat()
- pop()
- push()
- shift()
- unshift()
- splice()
新方法:set() 和 subarray()
- set(): 从提供的数组或者定型数组中把值复制到当前定型数组中指定的索引位置
- subarray():与set相反,基于从原始定型数组中复制的值返回一个新定型数组
const container = new Int16Array(8);
container.set(Int8Array.of(1,2,3,4));
console.log(container); //[1,2,3,4,0,0,0,0]
const source = Int16Array.of(2,4,6,8);
const halfCopy = source.subarray(2); //[6,8]
四、Map
键/值存储机制
4.1 基本API
const m =new Map([
["key1","val1"],
["key2","val2"],
["key3","val3"]
])
初始化后,可以使用set()方法在添加键值对。get()和 has()进行查询。size属性获取映射中的键值对的数量。delete()和clear()删除值
const m = new Map();
m.set("first","niki")
.set("last","elvira");
console.log(m.has("first")); //true
console.log(m.get("first"));//"niki"
4.2 顺序与迭代
Map实例会维护键值对的插入顺序,因此可以根据插入顺序执行迭代操作
映射实例提供一个迭代器(Iterator),能以插入的顺序生成[key,value]。通过entries()或Symbol.iterator属性取得该迭代器
const m =new Map([
["key1","val1"],
["key2","val2"],
["key3","val3"]
])
console.log([...]m); //解构
若不用迭代器,而使用回调方式,则可调用映射的forEach(callback,opt_thisArg)方法并传入回调,依次迭代每个键值对。传入的回调接收可选第二个参数,用于重写回调内部this的值
const m =new Map([
["key1","val1"],
["key2","val2"],
["key3","val3"]
])
m.forEach((val,key) => alert(`${key} -> ${val}`));
//[key1,val1]
//[key2,val2]
//[key3,val3]
4.3 选择Object还是Map
1. 内存占用
给定固定大小,Map大约可以比Object多存储50%的键值对
2. 插入性能
Map性能更佳
3. 查找速度
如果代码涉及大量查找操作,选择Object更好
4. 删除性能
Map的delete()操作都比插入和查找更快。
五、WeakMap
WeakMap 是 Map 的兄弟类型,其API是Map的子集。weak描述的是JS垃圾回收程序对待“弱映射”中键的方式
5.1 基本API
弱映射中的键只能是Object 或者继承自Object的类型,尝试使用非对象设置键会抛出TypeError。只要有一个键无效就会抛出错误
const key1 = {id:1},
key2 = {id:2},
key3 = {id:3};
const wm = new WeakMap([
["key1","val1"],
["key2","val2"],
["key3","val3"]
]);
console.log(wm.get(key1)); //val1
5.2 使用弱映射
1. 私有变量
弱映射造就了在JS中实现真正私有变量的新方式。前提:私有变量会存储在弱映射中,以对象实例为键,以私有成员的字典为值。
2. DOM节点元数据
因为WeakMap不会妨碍垃圾回收,所以非常适合保存关联元数据
const wm = new WeakMap();
const loginButton = docunment.querySelector('#login');
wm.set(loginButton,{disabled:true});
六、Set
6.1 基本API
const s1 = new Set(["val1","val2","val3"]);
初始化后,可以使用**add()**增加值,使用has()查询,通过size取得元素数量,使用delete()和clear()删除元素,delete()返回一个布尔值,表示集合中是否存在要删除的值
6.2 定义正式集合操作
- 某些Set操作是有关联性的,最好让实现的方法能支持处理多个集合实例
- Set保留插入顺序,所有方法返回的集合必须保证顺序
- 尽可能高效使用内存。尽可能避免集合与数组间相互转换能节省对象初始化成本
- 不要修改已有的集合实例
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/150435.html