剩余参数
剩余参数本质
// 剩余参数的本质
const add=(x,y,...args)=>{
console.log(x,y,args);
}
add();
add(1);
add(1,2);
add(1,2,3,4,5);
剩余参数的注意事项
- 箭头函数的参数部分即使只有一个剩余参数,也不能省略圆括号
- 使用剩余参数替代arguments获取实际参数
- 剩余参数的位置只能是最后一个参数,之后不能再有其他参数,否则会报错
// const add=function (){
// console.log(arguments);
// }
// 箭头函数不能用arguments对象
// const add=()=>{
// console.log(arguments);
// }
const add=(...args)=>{
console.log(args);
}
add(1,2);
特别注意:arguments对象在箭头函数不能使用
剩余参数的应用
剩余参数可以是数组也可以是对象
// 与解构赋值一起使用
// 剩余参数不一定非要作为函数参数使用
// 必须是最后一个
const [num,...args]=[1,2,3,4];
console.log(num,args);
const func=([n,...args])=>{
console.log(n,args);
};
func([1,2,3]);
// ...z代表剩余元素比较合适
const {x,y,...z}={a:1,x:2,y:3,b:4};
console.log(x,y,z); //z是一个对象
const func1=({x,y,...z})=>{
console.log(x,y,z); //z是一个对象
};
func1({a:1,x:2,y:3,b:4});
数组的展开运算符
Math.min()括号里面必须是参数列表,不能是数组,把它转为展开运算符即可使用
console.log(Math.min(...[1,2,3])); //1
console.log(Math.min(3,1,2)); //1
区分剩余参数和展开运算符
// 本质区别
// 展开运算符
// [3,1,2]->3,1,2
// 剩余参数
// 3,1,2->[3,1,2]
const add=(...args)=>{
console.log(args); //剩余参数
console.log(...args); //展开运算符
}
add(1,2,3);
// 二维数组转为一维数组
console.log([...[1,2,3],4]);
数组展开运算符的应用
// 1.复制数组
const a=[1,2];
const b=[...a];
a[0]=3;
console.log(a);
console.log(b);
// 2.合并数组
const c=[1,2];
const d=[3];
const e=[4,5];
console.log([...d,...c,...e]);
// 3.字符串转为数组
// 字符串可以按照数组的形式展开
console.log(...'alex');
console.log('a','l','e','x');
console.log([...'alex']);
console.log('alex'.split(''));
// 4.常见的类数组转化为数组
// arguments和NodeList
function func(){
console.log(arguments);
console.log([...arguments]);
}
func(1,2);
// NodeList
console.log(document.querySelectorAll('p'));
console.log([...document.querySelectorAll('p')].push);
对象的展开运算符
对象的展开:把属性罗列出来,用逗号分隔,放到一个{}中,构成新对象
// 1.展开对象
// 对象不能直接展开,必须在花括号里展开
const apple = {
color: '红色',
shape: '球形',
taste: '甜'
};
// 新对象
console.log({...apple});
console.log({...apple} === apple); //false
// 2.合并对象
const pen={
color: '黑色',
shape: '圆柱形',
use:'写字'
};
// 新对象拥有全部属性,相同属性,后者覆盖前者
console.log({...pen,...apple});
对象展开运算符的注意事项
// 1.空对象的展开
// 如果展开一个空对象,则没有任何效果
console.log({...{}});
console.log({...{},a:2});
// 2.非对象的展开
// 如果展开的不是对象,则会自动将其转为对象,再将其属性罗列出来
console.log({...1});
console.log(new Object(1)); // 对象Number{1},里面没有任何属性
console.log(new Object(undefined)); // {}.里面没有任何属性
console.log(new Object(null)); // {},里面没有任何属性
console.log(new Object(true)); // 对象Boolean{true},里面没有任何属性
// 如果展开运算符后面是字符串/数组,它会自动转成一个类似数组的对象
// 返回的不是空对象
console.log({...'alex'});
console.log([...'alex']);
console.log({...[1,2,3]});
// 3.对象中对象属性的展开
// 特点:不会展开对象中的对象属性
const apple={
feature:{
taste:'甜'
}
};
const pen={
feature: {
color:'黑色',
shape:'圆柱形'
},
use:'写字'
};
console.log({...apple});
console.log({...apple,...pen});
对象展开运算符的应用
// 用户参数和默认参数
const logUser=userParam=>{
const defaultParam={
username:'Alex',
age:0,
sex:'male'
}
// const param={...defaultParam,...useParam};
// console.log(param.username,param.age,param.sex);
const {username,age,sex}={...defaultParam,...userParam};
console.log(username,age,sex);
}
logUser();
logUser({username:'张三'});
调用logUser()方法,用户参数userParam为undefined,这时...userParam为{},所以{username,age,sex}跟默认参数一样
Set和Map数据结构
Set数据结构
Set是一系列无序、没有重复值的数据集合
方法有:add、has、delete、clear、forEach 属性有:size
const s=new Set();
s.add(1);
s.add(2);
s.add(3);
// Set中不能有重复的成员
s.add(1);
console.log(s);
// Set没有下标去标示每一个值,所以Set是无序的,
// 也不能像数组那样通过下标去访问Set的成员
// 遍历(按照成员添加进集合的顺序遍历)
s.forEach(function (value,key,set){
// Set中value和key相等 为了统一
console.log(value,key,set===s);
//console.log(this); // 非严格模式下undefined转为window
})
Set构造函数的参数
<p>1</p>
<p>2</p>
<p>3</p>
<script>
// 1.数组
const s = new Set([1, 2, 1]);
console.log(s);
// 2.字符串、arguments、nodeList、Set实例等
console.log(new Set('hi'));
function func() {
console.log(new Set(arguments));
}
func(1, 2, 1);
console.log(new Set(document.querySelectorAll('p')));
console.log(new Set(s) === s);
</script>
Set的注意事项
- 判断重复set方式
- 什么时候使用set
// 1.判断重复的方式
const s=new Set([1,2,1,NaN,NaN]);
console.log(1===1);//true
console.log(NaN===NaN);//false
// Set对重复值的判断基本遵循严格相等(===)
// 但是对于NaN的判断与===不同,Set中NaN等于NaN
console.log(s);
const s1=new Set();
s1.add({}).add({});
console.log({}==={}); //引用类型看内存地址是否相同
console.log(s1);
// 2.什么时候使用Set
// (1)数组或字符串去重时
// (2)不需要通过下标访问,只需要遍历时
// (3)为了使用Set提供的方法和属性
Set的应用
// 1.数组去重
const s=new Set([1,2,1]);
console.log(s);
console.log(...s);
console.log([...s]);
console.log([...new Set([1,2,1])]);
// 2.字符串去重
const s1=new Set('abcddcba');
console.log(s1);
console.log(...s1);
console.log([...s1].join(''));
// 3.存放dom元素
console.log(document.querySelectorAll('p'));
const s2=new Set(document.querySelectorAll('p'));
console.log(s2);
s2.forEach(function (element){
console.log(element);
element.style.color='red';
element.style.backgroundColor='yellow';
})
Map数据结构
Map和对象都是键值对的集合
对象一般用字符串当作键,因为是合法的标识符,通常引号可省略
const obj = {
'name': 'alex',
true: true,
[{}]: 'object'
};
console.log(obj);
console.log({}.toString());
// 基本数据类型:数字、字符串。布尔值、undefined、null
// 引用数据类型:对象([]、{}、函数、Set、Map等)
// 以上都可以作为Map的键
const m = new Map();
m.set('name', 'alex');
m.set(true, 'true');
m.set({}, 'object');
m.set(new Set([1, 2]), 'set');
m.set(undefined, 'undefined');
console.log(m);
// 1.set方法
// 使用set添加的新成员,键名如果已经存在,后添加的键值对覆盖前面的
const m=new Map();
m.set('age',18).set(true,'true').set('age',20);
console.log(m);
// 2.get方法
// get获取不存在的成员,返回undefined,不会报错
console.log(m.get('age'));
console.log(m.get(true));
// 3.has方法
console.log(m.has('age'));
// 4.delete方法
// 删除不存在的成员,什么都不会发生,也不会报错
m.delete('age');
m.delete('name');
console.log(m);
// 5.clear方法
m.clear();
console.log(m);
// 6.forEach方法
m.set('age',18).set(true,'true').set('age',20);
m.forEach(function (value,key,map){
console.log(value,key,map===m);
console.log(this);
})
// size属性
console.log(m.size);
Map构造函数的参数
// 1.二维数组
console.log(new Map([
['name','alex'],
['age',18]
]));
// 2.Set、Map等
// Set也必须体现出键和值
const s=new Set([
['name','alex'],
['age',20]
]);
console.log(new Map(s));
console.log(s);
// Map
const m1=new Map([
['name','alex'],
['age',22]
]);
console.log(m1);
const m2=new Map(m1);
console.log(m2);
console.log(m2===m1);
Map的注意事项
- 判断键名是否相同的方式
- 什么时候使用Map
// 1.判断键名是否相同的方式
// 基本遵循严格相等(===)
// 例外就是NaN,Map中NaN等于NaN
const m=new Map();
m.set(NaN,1).set(NaN,2);
console.log(m);
// 2.什么时候使用Map
// (1)只需要key->value的结构
// (2)需要字符串以外的值做键名
Map的应用
// 解构赋值
const [p1, p2, p3] = document.querySelectorAll('p');
//console.log(p1,p2,p3);
const m = new Map([
[p1, {
color: 'red',
backgroundColor: 'yellow',
fontSize: '40px'
}],
[p2, {
color: 'green',
backgroundColor: 'pink',
fontSize: '40px'
}],
[p3, {
color: 'blue',
backgroundColor: 'orange',
fontSize: '40px'
}]
]);
m.forEach((propObj, element) => {
for (const p in propObj){
element.style[p]=propObj[p];
}
});
遍历
// 使用iterator 可遍历对象
const it=[1,2][Symbol.iterator]();
console.log(it.next());
console.log(it.next());
console.log(it.next());
// Symbol.iterator: 可遍历对象的生成方法
我们一般不会直接使用iterator去遍历(Symbol.iterator->it->next())
遍历数组:for循环和forEach方法
遍历对象:for in循环
console.log([][Symbol.iterator]());
console.log({}[Symbol.iterator]);
for…of用法
- 与break、continue一起使用
- 在for…of中取得数组的索引
- for...of循环只会遍历出那些done为false时对应的value值
// 1.for...of循环本质
const arr=[1,2,3];
// const it=arr[Symbol.iterator]();
// let next=it.next();
// //console.log(next);
// while (!next.done){
// console.log(next.value);
// next=it.next();
// }
for (const item of arr) {
console.log(item);
}
// 2.用法
// 在for...of获得索引
// keys()得到的是索引的可遍历对象,可以遍历出索引值
console.log(arr.keys());
for (const key of arr.keys()) {
console.log(key);
}
// values()得到的是值的可遍历对象,可以遍历出值
console.log(arr.values());
for (const value of arr.values()) {
console.log(value);
}
// entries()得到的是索引+值组成的数组的可遍历对象
for (const entries of arr.entries()) {
console.log(entries);
}
// 解构赋值
for (const [index, value] of arr.entries()) {
console.log(index,value);
}
原生可遍历与非原生可遍历
只要有Symbol.iterator方法,并且这个方法可以生成可遍历对象,就是可遍历的
原生可遍历的有:数组、字符串、Set、Map、函数的arguments对象、NodeList
非原生可遍历的有:一般的对象、有length和索引属性的对象
const person={
sex:'male',
age:18
};
person[Symbol.iterator]=()=>{
let index=0;
return{
next(){
index++;
if (index===1){
return{
value:person.age,
done:false
}
}else if (index===2){
return {
value: person.sex,
done: false
}
}else {
return {
done: true
}
}
}
}
};
for (const item of person) {
console.log(item);
}
const obj={
0:'alex',
1:'male',
length:2
};
// obj[Symbol.iterator]=Array.prototype[Symbol.iterator];
obj[Symbol.iterator]=()=>{
let index=0;
return{
next(){
let value,done;
if (index<obj.length){
value=obj[index];
done=false;
}else {
value=undefined;
done=true;
}
index++;
return{
value,done
}
}
}
};
for (const item of obj) {
console.log(item);
}
使用了Iterator的场合
对原生可遍历的:数组、字符串、Set、Map、函数的arguments对象、NodeList
- 数组的展开运算符
- 数组的解构赋值
- Set和Map的构造函数
// 1.数组的展开运算符
console.log(...[1,2,3]);
console.log(...'str');
console.log(...new Set([1,2,3]));
// 2.数组解构赋值(不需要结构匹配)
// 形式:[...]
console.log([...'hi']);
const [a,b]='hi';
// 相当于const [a,b]=[...'hi'];
console.log(a,b);