1. rest参数
rest参数是我们在ES6中新增的语法格式 它可以解析任何数据类型 比如说数组 对象 以及我们后面学的集合
在我们使用rest参数的时候 我们需要在前面加上… 虽然知识点的名字叫做rest 但是名字我们自定义
我们使用rest解开数组或者对象
let arr = [11,22,33,55,99];
console.log(arr);
// 使用...arr 将这三个点放在数组的前面 等于把数组中的元素取出
console.log(...arr);
// 如果说取出对象 直接取出的时候 会产生报错
let obj = {
name : 'Eric',
age : 18
};
// console.log(...obj);
// 可以使用...拷贝一个对象 那么是可以取出的
let newObj = {
...obj
};
console.log(newObj);
// 数组去重
let arr1 = [1,7,3,0,1,0,7,9,2,0,1];
let set = new Set(arr1);
let newArr1 = [...set];
console.log(newArr1);
当然 rest也可以作为剩余参数使用 也就是我们所说的传递不定参数 和arguments类似
因为在我们ES6中 我们大多数时候使用的是箭头函数 但是箭头函数不能使用arguments
如果说 我们需要传递不定参数 那么我们是无法使用的
那么这会 我们可以使用剩余参数
剩余参数 使用的时候 需要在形参列表中有所体现 参数的名字自定义 但是参数前面要加上…
// 求和函数 一般情况下是N个加数
// let sum = (...value)=>{
// // console.log(value);
// let total = 0;
// value.forEach(element=>total += element);
// console.log(total);
// }
// sum(1,2,3,4,5,7);
// 传递普通参数
let sum = (a,c,...value)=>{
console.log(value);
let total = 0;
value.forEach(element=>total += element);
console.log(total);
}
sum(1,2,3,4,5,6,7);
// 错误的写法 如果说传递普通参数 那么剩余参数一定要在最后
// let sum = (...value,a,c)=>{
// console.log(value);
// let total = 0;
// value.forEach(element=>total += element);
// console.log(total);
// }
// sum(1,2,3,4,5,6,7);
rest参数和arguments参数的区别
1.arguments不能在箭头函数中使用 但是rest可以在箭头函数中使用
2.arguments传参的时候 我们不能在形参列表中传递参数 但是rest传参 我们必须在形参列表中使用 并且是…参数名

3.rest参数可以和常规参数一起进行传递 arguments不能 但是 如果说传递常规参数 那么rest参数必须放在最后
2. Symbol数据类型
定义
Symbol他是ES6新增的一种数据类型 它主要的作用是保证数据的唯一性
比如说对象属性的名字或者方法的名字 可能会出现一些冲突 那么我们可以使用Symbol进行解决
同时定义的两个Symbol 他们绝对不会相等
他不是对象 也不是函数 只是一种数据类型 但是使用的时候 还是需要调用
// 使用Symbol
let sym1 = Symbol();
// console.log(sym1);
// console.log(typeof sym1);
// console.log(sym1 == sym1); // true
let sym2 = Symbol();
// 两个Symbol绝对不相等
console.log(sym1 == sym2); // false
但是 我们在使用的时候 基本上使用的比较麻烦 因为我们无法确定Symbol()是什么 如果说这个Symbol作为对象的键
我们都无法确认对象的键具体的代表是什么
这中情况下 我们可以向Symbol内部传递一个参数 参数作为Symbol的标识
但是注意 即使传递的参数相同 那么两个Symbol还是不会相等
let name = Symbol('姓名');
let user = Symbol('姓名');
let age = Symbol('年龄');
console.log(name);
console.log(age);
console.log(user);
console.log(user == name); // false
注意 Symbol数据类型不能和其他任何数据类型进行运算
console.log(age + "abc");
console.log(age + 123);
console.log(age + 12.3);
console.log(age + true);
console.log(age + undefined);
console.log(age + null);
使用
有三种方式可以使用
1.直接在对象中使用
2.可以追加对象属性
3.使用defineProperty方法进行添加属性
<script>
let name = Symbol('姓名');
let age = Symbol('年龄');
let sex = Symbol('性别');
let school = Symbol('学校');
let like = Symbol('爱好');
let address = Symbol('地址');
let birth = Symbol('生日');
// 第一种方式 直接添加
let obj = {
[name] : 'Eric',
[age] : 18,
[sex] : '男',
[school] : '吉林大学',
[like] : ['吃','喝','旅游','女','学习','开车']
}
console.log(obj);
console.log(obj[age]);
console.log(obj[birth]);
// 第二种方式 向对象中追加属性
obj[address] = '吉林长春';
// 第三种方式 使用defineProperty方法进行追加属性
// 内置三个参数 第一个参数是原对象
// 第二个参数是Symbol
// 第三个参数是描述 描述还是一个对象 描述中有一个属性 属性是名value 值是新增属性值
Object.defineProperty(obj,birth,{
value : '2002-07-18'
})
console.log(obj);
</script>
3. set集合
set是ES6中新增的数据结构 他是一个无序的 不重复的集合 结构类似于数组
他是主要用来存储一些不重复的数据 或者作为数组去重使用
使用set集合必须先进行实例化 new 可以内置一个参数 也可以不传参数 参数是一个数组
内置方法
add : 添加一个集合元素
size属性 : 查看集合元素的数量 和数组的length一致
delete : 删除一个集合元素
has : 查看元素是否存在在这个集合中
clear : 清空集合
<script>
let arr = [1,7,3,0,1,0,7,9,2,0,1];
// 实例化一个set集合
let set = new Set(arr);
// let set1 = new Set();
console.log(set);
// console.log(set1);
// add : 添加一个集合元素
set.add(99);
console.log(set);
// delete : 删除一个元素
set.delete(99);
console.log(set);
// has : 查看一个元素是否存在在集合中
console.log(set.has(1));
console.log(set.has(5));
console.log(set.has(9));
console.log(set.has(99));
// size属性 : 查看集合中的元素的数量
console.log(set.size);
// clear : 清空集合
set.clear();
console.log(set);
</script>
数组的去重
<script>
let arr = [1,7,3,0,1,0,7,9,2,0,1];
// 首先去重数组 先定义一个set集合 将数组中的元素放到set集合中
// set集合会自动将数组的元素进行去重
let set = new Set(arr);
// console.log(set);
// 然后将set中的元素都取出来
// 注意 set不能使用for循环 或者是for...in进行遍历 需要使用for...of
// 但是我们可以使用剩余参数 解开集合 存储到新的数组中
let newArr = [...set];
console.log(newArr);
</script>
4. map集合
map也是ES6中新增的数据结构 也是一个无序的 不重复的集合 但是和set不同的是 set结构类似与数组
map的结构类似于对象 每一个元素是由键值对组成的
在使用的时候 也是和set一样 需要实例化 new Map 内置一个参数
参数是可选参数 如果说传递参数 那么参数是一个二维数组 每一个子数组内置两个元素 分别作为键值
内置方法
set : 添加一个map元素 内置两个参数 第一个参数是键 第二个参数是值
delete : 删除一个map元素
size属性 : 查看map中的元素数量
has : 查看元素是否存在map集合中
get : 获取一个map元素
clear : 清空map集合
<script>
let arr = [
['name','Eric'],
['age',18],
['sex','男'],
['address','吉林长春']
];
// 创建一个map 实例化
let map = new Map(arr);
console.log(map);
// set添加一个元素
map.set('birth','2002-7-18');
console.log(map);
// delete删除一个元素
map.delete('birth');
console.log(map);
// size 查看元素的具体数量
console.log(map.size);
// has 查看元素是否存在在map集合中
console.log(map.has('birth'));
console.log(map.has('name'));
// get 获取一个map集合元素
console.log(map.get('name'));
console.log(map.get('age'));
console.log(map.get('birth'));
// clear 清空集合
map.clear();
console.log(map);
</script>
5. iterator迭代器
简介 : iterator 是一种接口机制 为不同的数据结构提供统一的访问机制
作用
为不同的数据结构提供统一的访问机制
使数据结构成员能够按照某种次序排序 一一迭代出来
ES6提供了一个新的遍历办法 for of
Iterator主要是提供for of使用
原理
创造一个指针 指向数据结构的起始位置
第一次调用next() 指针自动指向数据结构的第一个成员
接下来不断的调用next() 指针一直往后移动 直到指向最后一个成员
每次调用next的时候 返回的值包括一个value done
如果说 指向数据 返回为真 {value : ‘值’, done : false}
如果说 指向最后了 没有值了 那么返回 {value : undefined , done : true}
原生具备iterator接口的数据(可以用for of遍历)
<script>
let arr = ['贾思勰','左丘明','司马光','司马迁','班超','辛弃疾','郦道元'];
/*
使用iterator接口机制迭代这个数组
首先 我们创建数组指针 指向数组的起始位置
每一次使用next方法进行调用 迭代数据
每一次next之后 那么数组指针下移 指向下一个元素
如果元素存在 有值 返回对象 {value : '值', done : false}
如果说指针移动到最后 那么没有值了 返回对象 {value : undefined , done : true}
首先 我们自定义一个函数 封装一个函数 主要是迭代数组使用
*/
function myIterator(object){
// 创建数组指针 指向数组中的第0个元素的位置 起始位置
let index = 0;
return {
next : function(){
if (object.length > index){
return {
value:object[index++],done:false};
}else{
return {
value:undefined,done:true};
}
}
}
}
let iter = myIterator(arr);
console.log(iter.next()); //{value : '贾思勰', done : false}
console.log(iter.next()); //{value : '左丘明', done : false}
console.log(iter.next()); //{value : '司马光', done : false}
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
let str = 'abcd';
let striter = myIterator(str);
console.log(striter.next());
console.log(striter.next());
console.log(striter.next());
console.log(striter.next());
console.log(striter.next());
</script>
iterator迭代的数据结构
其实在iterator中 我们是存在语法糖的 使用字符串 数组 set集合 map集合 中的Symbol.iterator 方法进行迭代
注意 iterator不能迭代对象 iterator的语法糖也不能迭代对象
<script>
let arr = ['贾思勰','左丘明','司马光','司马迁','班超','辛弃疾','郦道元'];
// 使用iterator迭代数组
// 首先调用[Symbol.iterator]方法
let arrIter = arr[Symbol.iterator]();
console.log(arrIter.next());
console.log(arrIter.next());
console.log(arrIter.next());
console.log(arrIter.next());
console.log(arrIter.next());
console.log(arrIter.next());
console.log(arrIter.next());
console.log(arrIter.next());
console.log('*'.repeat(50));
// 使用iterator迭代字符串
let str = 'abc';
let strIter = str[Symbol.iterator]();
console.log(strIter.next());
console.log(strIter.next());
console.log(strIter.next());
console.log(strIter.next());
console.log('*'.repeat(50));
// 使用iterator迭代set集合
let set = new Set(arr);
let setIter = set[Symbol.iterator]();
console.log(setIter.next());
console.log(setIter.next());
console.log(setIter.next());
console.log(setIter.next());
console.log(setIter.next());
console.log(setIter.next());
console.log(setIter.next());
console.log(setIter.next());
console.log('*'.repeat(50));
// 使用iterator迭代map集合
let map = new Map([['name','Eric'],['age',18]]);
let mapIter = map[Symbol.iterator]();
console.log(mapIter.next());
console.log(mapIter.next());
console.log(mapIter.next());
// 不能使用iterator迭代对象
let obj = {
name : 'Mary',
age : 13
};
let objIter = obj[Symbol.iterator]();
</script>
iterator的语法糖其实是for…of语句
for…of 可以迭代任何可迭代的数据类型 但是就是不能迭代对象
<script>
let arr = ['贾思勰','左丘明','司马光','司马迁','班超','辛弃疾','郦道元'];
// 使用for of迭代数组
// for(let value of arr){
// console.log(value);
// }
let str = 'abc';
// 使用for...of迭代字符串
// for(let value of str){
// console.log(value);
// }
let set = new Set(arr);
// 使用for...of迭代set集合
// for(let value of set){
// console.log(value);
// }
let map = new Map([['name','Eric'],['age',18]]);
// 使用for...of迭代map集合
for(let value of map){
console.log(value);
}
// 报错 : 所有的数据解构使用for...of 都可以迭代 但是 for...of不能迭代对象
// let obj = {
// name : 'Mary',
// age : 13
// };
// for(let value of obj){
// console.log(value);
// }
</script>
使用iterator迭代对象
<script>
let obj = {
name : 'Eric',
age : 18,
sex : '男'
};
// iterator迭代对象
obj[Symbol.iterator] = function(){
// 定义数组指针
let index = 0;
// 将对象中所有的键取出来组成一个数组
let keys = Object.keys(this);
return {
next : ()=>{
// 判断数组中的长度是否大于数组指针
if (keys.length > index){
return {
value : this[keys[index++]],done : false}
}else{
return {
value : undefined,done : true}
}
}
}
}
let iter = obj[Symbol.iterator]();
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
</script>
for…of和for…in和foreach之间的区别
for…of是iterator的语法糖 他能够遍历出iterator所有可迭代的数据类型和数据解构 set map 字符串 数组 不能迭代对象
foreach 只是遍历数组使用 不能用作其他 也不能迭代对象
for…in 是唯一一个可以迭代对象的一种语法结构 当然 也可以迭代数组 字符串
6. generator生成器
简介
ES6提供的解决异步编程的方案之一
Generator函数是一个状态机, 可控制函数执行过程
可暂停函数(惰性求值), 内部yield可暂停,外部调用next方法可启动
每次返回的是yield后的表达式结果
使用
定义generator需要在function后面加上*
调用函数不会打印任何函数数据,而是会返回一个指针对象
调用指针对象中的next方法,来执行generator函数中的内容
返回结果和iterator相似
使用
<script>
// generator函数 function后面需要加上一个* 证明是generator
function* myGenerator(){
yield "这是第一次";
yield "这是第二次";
yield "这是第三次";
yield "这是第四次";
yield "这是第五次";
return 'GAMEOVER';
}
let go = myGenerator();
console.log(go.next());
console.log(go.next());
console.log(go.next());
console.log(go.next());
console.log(go.next());
console.log(go.next());
console.log(go.next());
</script>
小案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
*{
padding: 0px;
margin: 0px;
}
#app{
width: 1200px;
margin: 0 auto;
/* border: 1px solid; */
}
#app div{
width: 1200px;
height: 44px;
line-height: 44px;
text-align: center;
font-size: 20px;
font-weight: 800;
margin: 5px 0px;
border: 1px solid;
}
#btn{
width: 800px;
height: 35px;
font-size: 18px;
display: block;
margin: 20px auto;
border: none;
outline: none;
}
</style>
</head>
<body>
<div id="app">
<div>显示1——20条数据</div>
</div>
<button id="btn">点击加载更多</button>
</body>
</html>
<script>
function* getData(){
yield "显示21——40条数据";
yield "显示41——60条数据";
yield "显示61——80条数据";
yield "显示81——100条数据";
yield "显示101——120条数据";
yield "显示121——140条数据";
yield "显示141——160条数据";
yield "显示161——180条数据";
yield "显示181——200条数据";
yield "显示201——220条数据";
}
let go = getData();
btn.onclick = function(){
let div = document.createElement('div');
let content = go.next().value;
if (content == undefined){
btn.style.display = 'none';
}else{
div.innerHTML = content;
app.appendChild(div);
}
}
</script>
7. promise
概念
Promise 是异步编程的一种解决方案,比传统的回调函数和事件更合理、更强大
ES6的Promise是一个构造函数, 用来生成Promise实例, Promise实例是异步操作管理者
Promise代表了未来某个将要发生的事件(通常是一个异步操作) 有了Promise对象
可以将异步操作以同步的流程表达出来, 避免了层层嵌套的回调函数(回调地狱)
Promise本身还是在使用回调函数(只不过比回调函数多了一种状态管理)
使用
创建promise对象,实例化Promise
参数是回调函数,内置两个参数,resolve和reject
promise三个状态
初始化状态 pending
成功状态 fullfilled
失败状态 rejected
调用
then : 触发resolve方法执行then
catch : 触发reject方法执行catch
<script>
/*
编辑一个Promise
Promise是一个构造函数 需要实例化才能使用 首先实例化Promise
内置一个参数 参数是回调函数
回调内置两个参数
第一个参数是成功状态 它本身是一个函数
第二个参数是失败状态 它本身也是一个函数
promise有一个返回值 返回值是一个promise对象
在promise内部 我们可以编辑三种状态
1.初始化状态
2.成功状态
3.失败状态
*/
let promise = new Promise((resolve,reject)=>{
// 定义一个初始值
let num = 9;
// 判定初始化状态
if (num > 99){
// 成功的话 我们可以调用成功状态 也就是resolve
// resolve本身是一个函数
// console.log(typeof resolve);
resolve('牵手了 成功');
}else{
// 失败的话 我们可以调用失败状态 reject 他也是一个函数
// console.log(typeof reject);
reject('分手了');
}
});
// 我们定义完的promise对象和没写一样 和定义的函数一样 没有调用
// 比如说成功状态的函数和是失败状态的函数也没有定义
// 如果说 我们触发promise 那么我们使用promise对象中的then方法进行触发
// then方法中内置一个或者两个参数 第一个参数是成功状态的函数
// 第二个参数是失败状态的函数 可选
promise.then((data)=>{
console.log(data);
},(error)=>{
console.log(error);
})
</script>
捕获异常
在使用then的时候是无法捕获异常的
但是在catch中可以捕获异常
try : 可能出现异常的代码
catch : 如果try出现错误,代码执行catch
finally : 不管会不会发生异常,都会执行finally中的代码
<script>
/*
异常处理
主要分为三个关键字 分别是try catch finally
try : 这个执行体中 存放着可能出错的代码
如果内部代码没有报错行为 那么直接执行 但是如果说代码报错了 那么执行catch里面的代码
catch : 准备一些代码 如果说try中的代码出错 我们去处理try中的代码
finally : 无论代码报错与否 那么都执行 比较鸡肋
*/
let num = 100;
try{
console.log(num);
// console.log('代码没出错的时候执行的');
}catch{
let num = 100;
console.log(num);
// console.log('try里面面有问题了 你想起我来了');
}finally{
console.log('怎么做我都执行');
}
</script>
链式操作
then方法的第一个参数,成功时的回调函数,分两种情况:
返回了一个普通的数据(非promise),这个值会作为参数传递给下一个then的成功回调
返回了一个promise,下一个then的执行,取决于这个promise状态的改变
// 捕获异常 详见17.html
// 使我们讲解异常处理 主要是对peomise进行链式操作
// 链式操作
let promise = new Promise((resolve,reject)=>{
let num = 9;
if (num > 99){
resolve('牵手了 成功');
}else{
reject('分手了');
}
});
// 当我们触发promise的时候 使用then方法
// 但是then方法中 我们一般情况下 只传递一个参数 参数是成功状态
// 我们then方法的返回值还是一个promise
// console.log(promise);
// 我们失败状态一般使用catch来处理 这是异常处理
promise.then((data)=>{
console.log(data);
}).catch((error)=>{
console.log(error);
})
如何使用promise处理回调地狱
<script>
let promise = new Promise((resolve,reject)=>{
resolve('京东的工艺');
})
// 我们使用promise对象then完之后 返回值依旧是promise
// 我们可以继续使用then进行触发
// 此时 新的返回值promise中的resolve状态(成功状态)就是刚刚的返回值
// 也就是说 我们触发时候的返回值 作为新的promise的成功状态 resolve状态
promise.then((data)=>{
console.log(data);
return data + '里面的打火机';
}).then(data=>{
console.log(data);
return data + "里面的zippo";
}).then(data=>{
console.log(data);
return data + "里面的黑色的";
}).then(data=>{
console.log(data);
return data + "青春靓丽";
}).then(data=>{
console.log(data + "的打火机");
})
</script>
面试题
谈一谈你对promise的理解?
答案: Promise用来解决异步回调问题,由于js是单线程的,很多异步操作都是依靠回调方法实现的,这种做法在逻辑比较复杂的回调嵌套
中会相当复杂;也叫做回调地狱;promise用来将这种繁杂的做法简化,让程序更具备可读性,可维护性;promise内部有三种状态,
pedding,fulfilled,rejected;pedding表示程序正在执行但未得到结果,即异步操作没有执行完毕,fulfilled表示程序执行完毕,且执
行成功,rejected表示执行完毕但失败;这里的成功和失败都是逻辑意义上的;并非是要报错。其实,promise和回调函数一样,都是要
解决数据的传递和消息发送问题,promise中的then一般对应成功后的数据处理,catch一般对应失败后的数据处理。