JavaScript ES6语法学习笔记 02、ES6语法扩展
@[toc]
前言
本篇博客是关于javascript的ES6语法扩展,若文章中出现相关问题,请指出!
所有博客文件目录索引:博客目录索引(持续更新)
一、剩余参数(多参=>数组,也有对象情况在对象解构赋值中)
1.1、认识与使用剩余参数
语法:...变量名
。剩余参数正常使用是永远都是数组,除非在对象解构赋值中使用会变为对象(称为剩余元素)。
实际简单应用:
//1、将调用函数传入的所有参数出存储在args数组中
const f = (...args) => console.log(args);
//2、将剩余的参数传入到args数组中
const f1 = (x, y, ...args) => console.log(args);
//测试
//1、无传入参数时,默认为空数组
f();
//2、传入指定数量参数,存储到数组中
f1(1, 2, 3, 4, 5);
1.2、箭头函数中使用剩余参数(代替arguments)
引出箭头函数中使用剩余参数
我们可以在箭头函数中使用剩余参数来代替普通函数中的arguments
,此时我们就有个疑问:箭头函数中没有arguments吗?
- 答案是正确的,在箭头函数里里没有this,其实也就没有了arguments,不过我们可以使用剩余参数来代替它,并且使用剩余参数比arguments参数更加强大,方法更多。(因为arguments是一个对象,其原型链中没有数组;而剩余参数是数组,自然也有就有了很多方法)
- ES6之后,完全可以使用剩余参数来替代arguments了!
测试一下箭头函数中没有arguments
参数:
const f = () => console.log(arguments);
实际案例
需求:在箭头函数中使用剩余参数进行参数的相加。
解决方案1:简单使用遍历来实现累加
//函数使用剩余参数实现相加
const add = (...args) => {
let sum = 0;
for (let i = 0; i < args.length; i++) {
sum += args[i];
}
console.log(sum);
};
add(1, 2, 3);//6
解决方案2:使用数组的reduce
方法来进行运算。
// reduce使用
const fun = (...args) => {
//回调函数中的第一个参数是上一次的返回值,第二个参数是当前遍历的值
return args.reduce((pre, value) => {
return pre + value;
}, 0);//第一次起始值为0
};
console.log(fun(1, 2, 3, 4, 5));//15
1.3、注意事项(3个)
1、箭头函数中若是只有一个参数为剩余参数,其不能省略()
。如:(...args) => console.log(args)
2、剩余参数的位置必须是在参数的最后一个位置,否则会抛出异常。
3、在进行对象解构赋值中使用剩余参数,实际上会形成一个对象
//在对象解构赋值中,...args表示获取剩余的所有键值形成对象
const { x, y, ...args } = {
a: 1,
x: 1,
z: 2,
t: 3
};
console.log(x, y, args);
二、展开运算符(数组=>多参,也有对象情况)
2.1、认识展开运算符
语法:...数组
或{...对象}
。前者可以将数组转为多个参数,后者可以将对象中的属性转为多个参数。
小测试一波:
注意点:若是想将展开的多个参数存储到数组中,就需要使用[]
包裹,如[...[1,2,3]]
,这样才能够构成数组。
2.2、针对数组(4个实用案例)
语法:
- 展开数组:
...数组
- 展开数组元素到数组中:
[...数组]
数组的展开运算符功能:
- 复制数组。实现浅拷贝。
- 合并数组。不再使用数组的concat()方法进行合并。
- 字符串快速转数组。不再使用字符串的split()方法。
- 类数组转为数组。让原本在类数组的元素转移到数组中,拥有数组的功能!
<script>
//1、复制数组:复制出来一个新的数组(浅拷贝)
const arr = [1, 2, 3, [1, 1]];
console.log([...arr]);//[1, 2, 3, Array(2)]
//2、合并数组:将多个数组进行合并 | 不需要使用concat()合并方法了
const arr1 = [1, 2];
const arr2 = [5, 6];
console.log([4, 5, ...arr1, ...arr2]);//[4, 5, 1, 2, 5, 6]
//3、字符串转为数组
console.log(...'changlu');//仅仅是将字符串展开:c h a n g l u
//转为数组应当放在[]中。 | 不需要再使用字符串方法split()了,转好之后直接使用数组的方法
console.log([..."changlu"]);//["c", "h", "a", "n", "g", "l", "u"]
//4、类数组转为数组
//①arguments:类数组
const f = function () {
console.log([...arguments])
};//将类数组转为数组后就能够使用更多方法
f(1, 2, 3, 4);//[1, 2, 3, 4]
//②NodeList转为数组
console.log([...document.querySelectorAll("div a")]);;
</script>
2.3、针对对象(语法与合并案例)
介绍语法
语法:对于对象展开必须使用{}包裹,如{...对象}
,不能够像数组一样。
<script>
const person = {
name: '人',
hobby: 'sport',
age: '未知',
thing: {
name: '物件',
mfunction: '功能'
}
};
//展开对象,查看内容
console.log({ ...person });
//测试是浅拷贝
console.log({ ...person } === person);
console.log({ ...person }.thing === person.thing);
</script>
应用:合并对象
记住一点:后面展开的对象属性若是与之前的属性重复会覆盖前面的属性!
<script>
//下面两个对象的name与hobby重复
const person = {
name: '人',
hobby: 'sport',
age: '未知'
};
const student = {
name: 'changlu',
hobby: 'play',
sex: '男'
};
//合并对象:后面展开的对象属性若是与前一个相同就会覆盖
console.log({ height: 180, ...person, ...student });
console.log({ height: 180, ...student, ...person });
</script>
三、Set对象
3.1、Set的基本方法与使用(四个属性、一个方法)
Set
:不能有重复的元素,没有下标表示对应的元素(也就意味不能使用下标来访问元素),是无序的。
包含四个方法与一个属性:
- 四个方法:
add()
、delete()
、clear()
、foreach()
。 - 属性:
size
。
<script>
var s = new Set();
//三个方法
//add():添加一个元素,不能添加重复的值(基本类型就是比较值;引用类型比较地址)
s.add(1);
s.add(1);//不能重复添加
s.add({});
s.add({});//可以添加,因为这两个对象是不同引用地址
//delete():删除指定元素
s.delete(1);//成功删除数字1
//clear():清除容器中的所有元素
s.clear();
//一个属性
//size:获取到当前set容器中存储的元素数量
console.log(s.size);
console.log(s);
</script>
foreach()
方法单独测试:需传入一个回调函数作为第一个参数,第二个参数对回调函数的this对象,默认为window对象。
- 回调函数可传入三个参数:第一个参数为当前访问的值;第二个参数为当前值的索引;第三个值为整个操作对象。
<script>
var s = new Set([1, 2, 3, 3, 4]);
//箭头函数与普通函数在回调函数中的this默认是window
s.forEach((currentvalue, index, s) => {
console.log("当前值:" + currentvalue, "当前索引:" + index, "操作集合:" + s, "this:" + this)
});//第二个参数可作为起始参数
//设置this对象,即设置foreach的第二个参数即可!
s.forEach(function (currentvalue, index, s) {
console.log(this)
}, document);
</script>
重点说明:使用forEach()
方法按照成员添加进集合中的顺序来进行遍历的。
3.2、Set构造函数的参数
参数可传入:数组、字符串、类数组如arguments
、NodeList
、Set
集合。
说明:对于自己本身set对象作为参数,实际上得到的值为浅拷贝!
<div>
<a href="">1</a>
<a href="">2</a>
<a href="">3</a>
</div>
<script>
//1、数组
const s = new Set([1, 2, 1]);
console.log(s);
//2、字符串
const s1 = new Set("changlu");
console.log(s1);
//3、类数组
//arguments
function fun() {
console.log(new Set(arguments));
}
fun(6, 66, 6);
//NodeList
console.log(new Set(document.querySelectorAll("div a")));;
//4、set对象
var s2 = new Set([1, 2]);
console.log(new Set(s2));
console.log(new Set(s2) === s2);//相当于进行浅拷贝
</script>
3.3、注意事项(2个)
1、判断重复的方式:Set对于重复值的判断基本遵循严格相等(===),有一个很特殊的值就是NaN,尽管NaN!==NaN
,在Set中同样不会出现两个NaN!
const s = new Set([NaN, 1, NaN]);
console.log(new Set(s));//Set(2) {NaN, 1}
2、何时使用Set
?
①数组或字符串去重时。
②为了使用Set
的提供的方法与属性时。
3.4、实际应用(3个小示例)
1、数组去重(最终存储到数组)
//数组通过set去重后再转为数组
const arr = [1, 2, 3, 3, 5, 5, 4, 6];
//通过使用数组展开式(普通的就是通过使用forEach一个个遍历push到数组里,不过这种方式更加方便)
console.log([...new Set(arr)]);//[1, 2, 3, 5, 4, 6]
2、字符串去重
<script>
const str = "chhhannngllu";
//首先去重后展开到数组,接着使用数组的join()方法合并为字符串
console.log([...new Set(str)].join(""));
</script>
3、对多个dom元素设置样式
思路:使用forEach()
方法进行遍历。
<div>
<a href="">1</a>
<a href="">2</a>
<a href="">3</a>
</div>
<script>
//获取三个a元素
var lists = document.querySelectorAll("div a");
//通过forEach的方式来进行统一设置颜色(其实也可直接通过数组的forEach方法进行)
new Set(lists).forEach((ele) => {
ele.style.color = "red";
ele.style.backgroundColor = "yellow";
});
</script>
四、Map对象
4.1、Map与对象的区别?
Map
与对象
都是键值对的集合,那么它们都有什么区别呢?
Map
:其键可以设置任意类型,如基本类型、引用类型都可以。对象
:其键只能是字符串,若是传入[]
对象,依旧会自动调用toString()转为字符串的!
示例:测试将对象插入到Map与对象中作为键
<script>
const o = {
name: 'changlu',
age: 18
};
//对象:对象的键一定是字符串,若是传入对象依旧会自动调用toString()转为字符串
const obj = {
[{}]: "hello",
[o]: '对象' //设置对象o为键
};
console.log(obj);
console.log({}.toString());//{}转为字符串形式就是"[object Object]"
//Map对象:Map的键可以为基本类型与引用类型,任意
const map = new Map();
map.set({}, "hello").set(o, "对象");
console.log(map);
</script>
4.2、Map的方法与属性
方法:set()、get()、has()、delete()、clear()、forEach()。
- get():若是获取不存在的成员,则会得到undefined。
- delete():若是删除不存在的成员,什么都不会发生。
属性:size。
<script>
const m = new Map();
//方法测试
//set():需要传入键值对,键可以是任意类型,若是相同时后面插入的值会覆盖前面一个
m.set("name", "changlu").set("name", "liner").set(undefined, "null");
//get():通过指定key来获取到value
const val = m.get(undefined)
console.log("获取key为undefined的值:", val);
//hash():传入key,测试是否包含这个键值对,不包含返回false,包含返回true
console.log(m.has(null));
console.log(m.has("name"));
//delete():传入key,删除指定键值对。若是没有指定键值对则无其他效果
m.delete(undefined);
m.delete(null);
//clear():清除所有元素
m.clear();
//forEach():遍历所有键值对。传入的回调函数以及第二个参数与set一致不用多说
m.set("name", "changlu").set("age", 18);
m.forEach((val, key, m) => {
console.log(val, key, m, "this:" + this);
}, document);
//属性测试
//size:获取当前集合中的元素数量
console.log(m.size);
console.log(m);
</script>
4.3、Map构造函数的参数
介绍与引出Map转数组效果
构造函数中的参数可传入:数组、Set
、Map
等等。
- 数组:二维数组,其中的一维数组放置键值对。
- Set:其中的每个元素应该是一个数组,数组中包含键值对。
- Map:相当于进行复制,就是浅拷贝。
首先在传入数组到构造函数中我们先来进行一个测试,看看将Map
展开得到什么:
<script>
//Map转数组(实际上转为二维数组,其中的一维数组装着键值对)
const m = new Map();
m.set("name", "changlu").set("age", 18);
console.log(...m);//得到两个数组
console.log([...m]);//将数组放置在数组中就得到了一个二维数组
</script>
OK此时我们大概心里有数了,传入的数组应当是二维数组了。
测试构造函数中的参数为:数组、Set、Map
<script>
//1、数组
const arr = [
['name', "changlu"],
['age', 18]
];
var m = new Map(arr);
console.log(m);
//2、Set:集合中的每个元素应当都是一个一维数组,一维数组中为键值对
const s = new Set(arr);
var m = new Map(s);
console.log(m);
//3、Map:进行浅拷贝
const arr1 = [
['name', "changlu"],
['thing', {
name: '东西'
}]
];
var m = new Map(arr1);
var m1 = new Map(m);
console.log(m);
console.log(m.get('thing') === m.get('thing'));//使用拷贝后的与拷贝前的进行严格等于若是为true表示浅拷贝
</script>
4.4、Map注意事项(2个)
1、判断键名是否相同的方式:对于NaN,在Map中也会认为其是相等的所以当添加多个Nan作为键的只能取最后一个。
const arr = [
[NaN, '1'],
[NaN, '2']
];
//对于NaN,在Map中也只取一个
const m = new Map(arr);
console.log(m);//Map(1) {NaN => "2"}
2、什么时候使用Map
?
从概念上来看:Map我们可以看做是容器,而对象则是用来描述模拟现实世界的实体;
从使用上:需要key、value结构来存储多个数据或者需要字符串以外类型的值作为键,我们可以使用Map!
4.5、实际应用(2个)
示例1
需求:对多个p元素的文字颜色进行设置,要求使用map结构来进行统一设置。
<script>
const [p, p1, p2] = document.querySelectorAll("div a");
//统一添加到map中
const map = new Map();
map.set(p, "green").set(p1, "red").set(p2, "blue");
//使用for each来快速遍历赋样式
map.forEach((color, ele) => {
ele.style.color = color;
});
</script>
示例2:在示例1的基础上,现在我们不仅仅添加一个属性了,而是多个属性,同样使用map来解决。
分析:将多个属性放置在对象中作为指定dom的值,之后通过进行遍历每个键值对时再遍历对象中的属性对dom元素进行样式赋值!
<script>
const [p, p1, p2] = document.querySelectorAll("div a");
//统一添加到map中
const map = new Map();
map.set(p, {
color: "red",
backgroundColor: "yellow",
}).set(p1, {
color: "blue",
backgroundColor: "green",
}).set(p2, {
color: "white",
backgroundColor: "black",
});
//使用for each来快速遍历赋值样式
map.forEach((styles, ele) => {
//对象进行遍历,styleName是对象中每个键值对的键
for (const styleName in styles) {
//这样就实现了多个属性同时赋值
ele.style[styleName] = styles[styleName];
}
});
</script>
五、遍历器iteractor与for…of循环
5.1、遍历器iteractor(ES6新增属性symbol,为for…of作铺垫)
5.1.1、iteractor三提问:什么?哪里?如何获取?
什么是iteractor
?iteractor
在哪里?怎么获取?
iteractor
是一个遍历器(或称迭代器),能够进行遍历获取集合中的值。- 其存在与数组、类数组、字符串等除了自定义对象。(自定义对象可以自定义)
- 通过调用
[Symbol.iterator]()
这个函数方法(其实就是values()
方法)获取到指定对象或集合的迭代器。
<script>
//在数组原型链对象中的"Symbol.iterator"是一个函数叫做values()
//调用[Symbol.iterator]()相当于调用values()方法返回一个Iteractor对象
console.log([1, 2][Symbol.iterator]);
console.log([1, 2][Symbol.iterator]());
</script>
5.1.2、iteractor初使用+快速遍历
初次认识iteractor对象的next()方法
既然说它是一个遍历器,那么我们如何来使用iteractor
来遍历数组中的元素呢?
- 通过使用遍历器的
next()
方法来进行遍历。
<script>
//获取到[1,2]数组的迭代器
const m_iteractor = [1, 2][Symbol.iterator]();
//next():返回一个对象,包含value与done属性,value就是对应数组中的元素值,done表示是否遍历结束,没有为false
console.log(m_iteractor.next());;
console.log(m_iteractor.next());;
//若是没有元素再进行遍历了,那么value值为undefined,done为true,表示结束了
console.log(m_iteractor.next());;
console.log(m_iteractor.next());;
</script>
快速遍历数组:利用next()方法获取到的两个值来进行
分析:现在外层调用next()
获取对象,接着使用while来进行遍历通过判断对象中的done是否为true作为条件!
<script>
//获取到[1,2]数组的迭代器
const m_iteractor = [1, 2][Symbol.iterator]();
//首先获取到第一次得到的next()函数执行后返回的对象
let f_next = m_iteractor.next();
console.log(f_next);
while (!f_next.done) {//一旦为true,则停止遍历
console.log(f_next.value);
f_next = m_iteractor.next();//继续进行遍历
}
</script>
5.1.3、iteractor的用途
遍历数组:for
循环和调用forEach()
方法。
遍历对象:for in
循环遍历得到key
。
此时我们思考一下对于数组、其他对象集合是不是缺了一种遍历的方式呢?
- 此时
iteractor
不就来了,其是一个统一的遍历方式。
应用场景:真正实际场景中我们并不会使用iteractor
来进行遍历的,但是很多方法或遍历方式都是在iteractor
之上进行封装的如for...of
、展开运算符底层就是使用了iteractor
。
5.2、for…of遍历方法
5.2.1、for…of使用(搭配break、continue)
本部分就拿数组下手,下面是使用for…of遍历数组以及搭配break、continue的案例:
<script>
const arr = [1, 2, 3, 4, 5];
console.log("for...of遍历:")
//val就是值
for (const val of arr) {
console.log(val);
}
//搭配break、continue使用
//需求:跳过值为2不输出,输出3就结束
console.log("搭配break、continue测试:");
for (const val of arr) {
if (val === 2) {
continue;
} else if (val === 4) {
break;
}
console.log(val);
}
</script>
5.2.2、for…of取得数组值与索引(单独值、单独索引、索引与值)
使用的是数组原型链中的方法:这些方法的返回值都是iteractor,所以都能够使用for...of
方法进行遍历
- 单独值:
values()
或arr[Symbol.iterator]()
- 单独索引:
keys()
。 - 索引与值:
entries()
。
<script>
const arr = [1, 2, 3, 4, 5];
//获取迭代器(两种方式):执行函数都能够获取到迭代器,迭代器中调用next就是获取到的值
console.log(arr[Symbol.iterator]());
console.log(arr.values());
//获取数组中的值:values()
for (const val of arr.values()) { //使用arr.values与arr的效果一致
console.log(val);
}
//获取数组中的索引:keys()
console.log(arr.keys());//返回值同样是数组迭代器:IterableIterator<number>
for (const index of arr.keys()) {
console.log(index);
}
//获取值与索引:entries()
console.log(arr.entries());//同样也是一个迭代器:IterableIterator<[number, number]>
//for (const entry of arr.entries()) { //可以更加优化,直接进行解构赋值
for (const [index, val] of arr.entries()) {
//entry是一个数组,装有索引与值
console.log(entry[0], entry[1]);
}
1 3 5 => 0 1 2
1=>5 2356
</script>
5.2.3、原生可遍历与非可遍历(有哪些)
介绍可遍历是什么以及有哪些?
什么是可遍历?只要有Symbol.iteractor
方法或者其他方法生成的可遍历对象,就都是可遍历的!
那么也就是说只要生成的是iteractor
对象就能够使用for...of
循环来统一遍历!
允许for…of遍历的:数组、字符串、Set
、Map
、arguments
、NodeList
。
非原生可遍历:对象(自定义的)。
示例(可遍历与非可遍历)
可遍历示例:上面所提及的允许遍历的都是原型链中具有Iteractor
对象(可调用方法获取的):
<script>
//数组
console.log("遍历数组:");
for (const val of [1, 2, 3]) {
console.log(val);
}
//字符串
console.log("遍历字符串:");
for (const ch of "changlu") {
console.log(ch);
}
//遍历Set
console.log("遍历Set:");
for (const val of new Set([1, 2])) {
console.log(val);
}
//遍历Map
console.log("遍历Map:");
for (const entry of new Map([["name", "changlu"], ["age", 18]])) {
//entry是一个一维数组,存放了键与值
console.log(entry);
}
//遍历类数组arguments
function fun() {
//arguments实际是类数组
for (const val of arguments) {
console.log(val);
}
}
console.log("遍历类数组arguments:");
fun(1, 1);
</script>
非可遍历示例:普通对象与类数组两种自定义迭代器
针对于普通对象:
<script>
const obj = {
name: 'changlu',
age: 18
};
console.log(obj);//自定义对象本身并没有iterator
//自定义symbol.iterator,此时就能够支持for...of
obj[Symbol.iterator] = () => {
let index = 0;
//返回一个对象,对象中包含next()方法,该方法返回一个对象value与done
return {
next() {
index++;
if (index === 1) {
return {
value: obj.name,
done: false
};
} else if (index === 2) {
return {
value: obj.age,
done: false
};
} else {
return {
value: undefined,
done: true
};
}
}
};
};
//原生遍历
const iter = obj[Symbol.iterator]();
console.log(iter.next());;
console.log(iter.next());;
console.log(iter.next());;
//for...of来遍历自定义对象
for (const val of obj) {
console.log(val);
}
</script>
类数组:有length和索引属性的对象
<script>
//类数组中的包含索引对应属性以及一个length
const obj = {
0: 'changlu',
1: 'liner',
2: '爱情',
length: 3
};
console.log(obj);//自定义对象本身并没有iterator
//自定义Symbol.iterator函数变量,调用返回一个对象包含next()方法,根据索引来遍历
obj[Symbol.iterator] = () => {
let index = 0;
return {
next() {
if (index === obj.length) {
return {
value: undefined,
done: true
};
} else {
return {
value: obj[index++],
done: false
};
}
}
};
}
//原生遍历
const iter = obj[Symbol.iterator]();
console.log(iter.next());;
console.log(iter.next());;
console.log(iter.next());;
console.log(iter.next());;
//for...of来遍历自定义对象
for (const val of obj) {
console.log(val);
}
</script>
5.3、实际使用iterator场合
1、数组、字符串、Set等的展开元素符,自定义对象不能进行展开(除非自定义遍历器)。
<script>
//类数组中的包含索引对应属性以及一个length
const obj = {
0: 'changlu',
1: 'liner',
2: '爱情',
length: 3
};
console.log({ ...obj });//对于自定义对象使用...若是包裹在{}是可以进行复制的
console.log(...obj);//直接使用...obj,由于自定义对象是没有遍历器所以会报错,除非自定义遍历器为自定义对象
</script>
2、数组以及其他可遍历类型进行解构赋值使用到了iterator
。
3、Set
与Map
的构造函数传入的参数也使用到了iterator
。
- 点赞
- 收藏
- 关注作者
评论(0)