ES6新特性学习

ES6新特性学习

ES6介绍

ES6全称ECMAScript6,是javaScript(ES5)的扩展,在ES5的基础上,ES6新增了一些新特性,比如变量提升,内置对象更加灵活,模块化更加完善。也是各大框架的底层常用的。

babel工具可以将es6代码转成es5代码,从而让浏览器获得支持

let和const关键字

let和const是在es5的基础上新增的变量关键字,与var关键字类似,区别在于,let关键字更加的严谨。

let的特点:没有变量提升,属于块级作用域,也不能重复声明,但是可以修改值。不会污染全局变量,解决循环遍历中的遍历变量的提升

const的特点:没有变量提升,属于块级作用域,也不能重复声明,也不可以修改普通值,但是可以修改对象的值。不会污染全局变量,解决循环遍历中的遍历变量的提升

首先来看原先的var关键字

/*
由于var声明的变量,会被js引擎提升,也就是说即时先打印a,再赋值a,最后再声明a,js引擎解释代码,也是会先声明,后赋值,然后打印,所以这段代码,再哪里打印都不会报错*/
console.log(a);// 不会报错
a = 10;
var a;
console.log(a);// 不会报错

let关键字

接着来看let关键字

// 1.let 声明变量,变量不会提升 所以先打印,后声明赋值会报错
console.log(a);// 报错
let a = 10;
// 2.let声明的变量是个块级作用域,所以仅在函数内部能狗访问到,再函数外部就访问不到了,所以报错
console(b);// 报错
// 是一个块级作用域
if(1===1){
    var b = 10;
}
console(b);// 报错
// 3.let声明的变量,不能重复声明
let c = 1;
let c = 3;
console.log(c);// 会报错

const关键字

再来看const关键字

/*
	1.const声明的变量,也不会提升,所以先打印后声明赋值会报错
*/
console.log(max);// 报错,变量不能提升
const max = 10;
// 2.const声明的变量不能修改值(除对象内部的值外),所以会报错
const max = 10;
max = 40;
console.log(max);// 报错,不能修改值
// 3.const声明的变量是个块级作用域,所以仅在函数内部能狗访问到,再函数外部就访问不到了,所以报错
if(1===1){
    // const声明的常量属于块级作用域
    const max1 = 30;
}
console.log(max1);//报错,max1的作用域仅在函数内部
// 4.但是const可以修改对象内部的值
const person = {
    name:'aaa'
}
// 可以修改person对象中的name的值
person.name = 'bbb';

let和const关键字的作用

// 作用1:解决for循环中,循环变量的变量提升问题
const arr = [];
for(let i=0;i<10;i++){
    arr[i] = function(){
        return i;
    }
}
console.log(arr[5]());// 输出5
/*
这里的可以发现,如果说用var遍历的话,那这段遍历的代码中的i的最终结果都会为10,但是用let关键字来遍历的话,那就是遍历到哪,i就是哪个,所以arr[5]() 的结果就是5,而不是10
*/
//作用2:不会污染全局变量
let RegExp = 10;
console.log(RegExp);//10
console.log(window.RegExp);

模板字符串

模板字符串,使用反引号,插入变量时使用$(变量名)

先来看看原生的写法

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>模板字符串</title>
	</head>
	<body>
		<div class="box"></div>
		<script>
			// 模板字符串,使用反引号,插入变量时使用$(变量名)
			const oBox = document.querySelector('.box');
			let id=1,name ='小马哥';
			
			//原始的写法,在html中插入变量
			oBox.innerHTML = "<ul><li><p id=" + id + ">" + name + "</p></li></ul>"
		</script>
	</body>
</html>

更简便的写法(推荐使用)

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>模板字符串</title>
	</head>
	<body>
		<div class="box"></div>
		<script>
			// 模板字符串,使用反引号,插入变量时使用$(变量名)
			const oBox = document.querySelector('.box');
			let id=1,name ='小马哥';
			// 更简便的写法
			let htmlStr = `<ul>
				<li>
					<p id="${id}">${name}</p>
				</li>
			</ul>`;
			oBox.innerHTML = htmlStr;	
		</script>
	</body>
</html>

函数

ES6中的函数,新增了新的写法,来简化了ES5的代码

带参数默认值的函数

// 1.带参数默认值的函数
// 如果调用该函数时,不传入参数,那就会按默认值来操作
function add(a=10,b=20){
    return a+b;
}
console.log(add());//30

参数为一个函数的函数

// 2.默认的表达式也可以是一个函数
function add1(a,b=getVal(5)){
    return a+b;
}
function getVal(val){
    return val+5;
}
console.log(add1(10));//20

剩余参数

由三个点,和一个紧跟着具名参数指定(…keys),keys的名字可以自定义

keys 是一个数组,里面包含了传进来的参数,解决了argumnets伪数组的问题

function checkArgs(...args){
    console.log(args); // 会打印['a','b','c']
}
checkArgs('a','b','c');

扩展运算符

原先的剩余运算符:把多个独立的参数合并到一个数组中,
扩展运算符:将一个数组分割,并将各个项作为分离的参数传给函数

// 案例:处理数组中的最大值
let arr = [10,20,50,30,90,100,40];
// 将上面的arr变量以...的形式传递给函数
console.log(Math.max(...arr)); // 从arr中找出最大值

箭头函数

使用 => 来定义 function(){} 等价于 ()=>{}

let add = (a,b)=>{
    return a + b;
}
// 上述代码等价于
// let add = function(a,b){return a + b;}

更简便的写法

一个参数的情况
// 一个参数的情况
let add1 = val => (val);
// 上述代码等价于
//let add1 = function(val){return val};
多个参数的情况
// 多个参数的情况
let add2 = (val1,val2) => (val1+val2); 
// 上述代码等价于
// let add2 = function(val1,val2){return val1+val2;}
无参的情况
// 无参的情况
let fn = ()=>'hello world'; 
// 上述代码等价于
//let fn = function(){return 'hello world'};
返回对象情况
// 返回对象情况
let getObj = id => ({id:id,name:'小马哥'});
// 上述代码等价于
/*
let getObj = id =>{
    return {
        id:id,
        name:'小马哥'
    }
}
*/

let obj = getObj(1);
console.log(obj);
闭包的情况
// 闭包的情况
let fn = (()=>{
    return ()=>{
        console.log('箭头函数闭包形式');
    }
})();
// 上述代码等价于
/*
let fn = (function(){
    return function(){
        console.log('箭头函数闭包形式');
    }
})()
*/

fn();
箭头函数的this指向

在ES6中,箭头函数没有this指向,箭头函数内部this值只能通过查找作用域链来确定

使用箭头函数的注意事项
1.使用箭头函数,函数内部不存在arguments
2.使用箭头函数,不能使用new关键字来实例化对象

// this指向
// 在ES6中没有this指向的绑定
let PageHandle = {
    id:123,
    // 给方法定义的时候,不要使用箭头函数
    init:function(){
        document.addEventListener('click',(event)=>{
            // 箭头函数没有this指向,箭头函数内部this值只能通过查找作用域链来确定,也就是说哪个对象调用,this就指向哪个对象
            // 此时this指向了 PageHandle 对象
            this.doSomeThings(event.type);
        },false)
    },
    doSomeThings:function(type){
        console.log(`事件类型:$(type),当前id:${this.id}`);
    }
}
PageHandle.init();

解构赋值

解构赋值是对赋值运算符的一种扩展,它针对数组和对象来进行操作,优点:代码书写上简洁易读

不使用解构的时候,取值要一个一个取

let node = {
    type:'iden',
    name:'foo'
}

// 不使用解构赋值的时候
let type = node.type;
let name = node.name;

而使用解构的时候,取值就可以一次性全部取出

完全解构

// 完全解构:解构赋值
let {type,name} = node; // 取出node中的type和name的值
console(type,name);

不完全解构

let obj = {
    a:{
        name:'zhangsan'
    },
    b = [],
        c : 'hello world'
}
// 不完全解构:只取出obj中的a的值
let {a} = obj;

剩余运算符解构

// 剩余运算符解构
let obj = {
    a:{
        name:'zhangsan'
    },
    b = [],
        c : 'hello world'
}
let {a,...res} = obj;
console.log(res);

默认值

// 默认值
let {a,b=30} = {a:20};// 设置a的默认值20,b默认值30

对数组解构

// 对数组解构
let arr = [1,2,3];
let [a,b,c] = arr;
console.log(a,b,c);// [1,2,3]

对嵌套数组解构

// 可嵌套
let[a,[b],c] = [1,[2],3];

扩展的对象的功能

直接写入变量和函数

es6直接写入变量和函数,作为对象的属性和方法

// 案例1
const name = '小马哥',age = 20;
const person = {
    name,// 等价于 name:name
    age, // 等价于 age:age
    sayName(){ // 等价于 sayName:function(){}
        console.log(this.name);
    }
}
person.sayName();
// 案例2
function fn(x,y){
    return {x,y}; // 等价于 return{x:x,y:y}
}
fn(10,20);
// 案例3
let cart = {
    wheel:4,
    set(newVal){
        if(newVal < this.wheel){
            throw new Errop('轮子数太少了');
        }
        this.wheel = newVal;
    },
    get(){
        return this.wheel;
    }
}
cart.set(3);
console.log(cart.get());
// 案例4
const obj = {};
obj.isShow = true;
const name = 'a';
obj[name+'bc'] = 123;
console.log(obj);//{isShow: true, abc: 123}
obj['f'+'bc'] = function(){console.log(this)};
console.log(obj); //{isShow: true, abc: 123, fbc: ƒ}
// 案例5
const name = 'a';
const obj = {
    isShow:true,
    [name+'bc']:123,
    ['f'+name](){
        console.log(this);
    }
}
console.log(obj);

对象的方法

Object.is() 相等于 === :比较两个值是否严格相等,比===更加严谨

Object.assign(target,obj1,obj2,obj3,…) : 对象的合并 返回合并之后的新对象,浅拷贝

is()方法
// 对象的方法
// 1.is() 相等于 === :比较两个值是否严格相等,比===更加严谨
console.log(Object.is(NaN,NaN));

assign()方法
//2. assign() 对象的合并 返回合并之后的新对象,浅拷贝
// Object.assign(target,obj1,obj2,obj3,...);
let newObj = Object.assign({},{a:1},{b:2});
console.log(newObj); // {a:1,b:2} 将多个对象合并了

Symbol类型

原始数据类型 Symbol ,表示独一无二的值,最大的用途:用来定义对象的私有变量,常见于框架中,应用型开发不常见

Symbol的定义

Symbol(‘值’)

const name = Symbol('name');
const name2 = Symbol('name');
// 使用Symbol类型,表示是在不同的内存地址
console.log(name === name2);// 内存地址不同,所以结果为fasle

Symbol的取值

对象[变量名]

let s1 = Symbol('s1');
console.log(s1);
let obj = {
    [s1]:'小马哥'// 一定要用[]来定义
};
obj[s1] = '小马哥';
// 如果用Symbol定义的对象中的变量,取值时一定要用[变量名]
console.log(obj[s1]);

Symbol的遍历

Symbol定义的数据,是无法用循环来遍历的,要用Object中的getOwnPropertySymbols和Reflect中的ownKeys来获取

// 1.可以用Object.getOwnPropertySymbols(变量)来获取,然后用索引获取具体的值
let s = Object.getOwnPropertySymbols(obj);
console.log(s[0]);

// 2.也可以使用 Reflect.ownKeys(变量) 来获取
let m = Reflect.ownKeys(obj);
console.log(m);

Set集合

集合:表示无重复的有序列表

创建set集合对象

// 1.创建set集合对象
let set = new Set();
console.log(set);

添加元素

// 2.添加元素
set.add(2);// 添加数值
set.add('4');// 添加字符串
set.add('4');// 会忽略重复值
set.add([1,2,3]);// 添加数组
console.log(set); // Set(3) {2, '4', Array(3)}

删除数据

// 3.删除数据 .delete(要删除的数据)
set.delete(2);
console.log(set); // Set(2) {'4', Array(3)}	

校验是否有值

// 4.has() 方法校验某个值是否在set集合中
console.log(set.has('4'));

获取数组的长度

// 5.size 获取数组的长度
console.log(set.size);

将set转换成数组

// 6.将set转换成数组
let set2 = new Set([1,2,3,3,3,4]);
let arr = [...set2]; // 通过扩展运算符 转成数组
console.log(arr);
// set的循环遍历无意义,键值对都是一致的
// 7.set.forEach((val,key)=>{});

set的无法释放

// set中对象的引用无法被释放
let set3 = new Set(),obj = {};
set3.add(obj);
// 释放当前资源,会发现无法释放
obj = null;

解决方式,可以用WeakSet对象

WeakSet对象

WeakSet的特点
1.不能传入非对象类型的参数
2.不可迭代
3.没有forEach()
4.没有属性

// 解决无法释放资源的方式:使用WeakSet()对象
let set4 = new WeakSet(),obj = {};

Map类型

Map类型是键值对的有序列表,键和值可以是任意值

创建Map对象

// 1.创建map对象
let map = new Map();

添加数据

// 2.添加数据
map.set('name','张三');
map.set('name','张三'); // 忽略重复的数据
map.set('age',20);
console.log(map); //Map(2) {'name' => '张三', 'age' => 20}

删除数据

// 3.delete('键') 删除数据
map.delete('name');

清空数据

// 4.clear() 直接清空数据
map.clear()

判断是否有指定的值

// 5.has() 判断是否有指定的值
map.has('name'); // true

键和值可以是任意类型

// 6.键和值可以是任意类型
map.set(['a',[1,2,3]],'hello');
console.log(map); //  Map(2) {'age' => 20, Array(2) => 'hello'}

初始化时赋值

// 7.在初始化的时候就赋值,不易阅读
let m = new Map([
    ['a',1],
    ['c',2],
]);
console.log(m);

Map集合也不能释放,要释放用WeakMap

数组的扩展方法

Array.form()方法

Array.form(伪数组,回调函数) 将伪数组转换成真正的数组,第二个参数,用来对每个元素进行处理

// 1.Array.form(伪数组,回调函数) 将伪数组转换成真正的数组
// 应用:将获取到的dom伪数组转成数组
function add(){
    // 将伪数组转换成真正的数组
    let arr = Array.from(arguments);
}

通过扩展运算符 将伪数组转成真正的数组

// 通过扩展运算符 将伪数组转成真正的数组
console.log([...lis]);
// Array.from() 还可以接收第二个参数,用来对每个元素进行处理
let liContents = Array.from(lis,ele => ele.textContent);
console.log(liContents);

Array.of()

Array.of() 将任意的数据类型,转成数组

// 2.Array.of() 将任意的数据类型,转成数组
console.log(Array.of(3,11,20,'30'));

copyWithin()

// 3.copyWithin() 数组内部将指定位置的元素复制到其他的位置,返回当前数组
// 将索引为3的后面的全部数,替换成索引为0往后3位
[1,2,3,8,9,10].copyWithin(0,3);//[8,9,10,8,9,10]

find()和findIndex()

// 4.find(n=>条件) findIndex(n=>条件)
// find() 找出第一个符合条件的数组成员
// findIndex() 找出第一个符合条件的数组成员的索引
[1,2,-10,-20,9,2].find(n=>n<0);

entries() keys() values()

keys() 对键名的遍历
values() 对值的遍历
entries() 对键值对的遍历

返回一个遍历器,可以使用for…of循环进行遍历

// 5.entries() keys() values() 返回一个遍历器,可以使用for...of循环进行遍历
// keys() 对键名的遍历
// values() 对值的遍历
// entries() 对键值对的遍历

// 遍历键
for(let index of ['a','b'].keys()){
    console.log(index); // index 获取索引:0,1
}
// 遍历值
for(let index of ['a','b'].values()){
    console.log(index); // index 获取值:a,b
}
// 遍历键值对
for(let [index,ele] of ['a','b'].entries()){
    console.log(index,ele); // 0 "a" 1 "b"
}

// 数组的遍历器
let letter = ['a','b','c'];
let it = letter.entries();
console.log(it.next().value);// 遍历第一次 [0,"a"]
console.log(it.next().value);// 遍历第二次 [1,"b"]
console.log(it.next().value);// 遍历第三次 [2,"c"]
console.log(it.next().value);// 遍历第四次 undefined
			

includes()

返回一个布尔值,表示某个数组是否包含给的值

// 6. includes() 返回一个布尔值,表示某个数组是否包含给的值
console.log([1,2,3].includes(2));// true 包含2
console.log([1,2,3].includes('4'));// false 不包含'4'

完整代码

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>数组的扩展方法</title>
	</head>
	<body>
		<ul>
			<li>1</li>
			<li>2</li>
			<li>3</li>
			<li>4</li>
		</ul>
		<script>
			// 数组的方法 from of()
			
			// 1.Array.form(伪数组,回调函数) 将伪数组转换成真正的数组
			// 应用:将获取到的dom伪数组转成数组
			function add(){
				// 将伪数组转换成真正的数组
				let arr = Array.from(arguments);
			}
			
			let lis = document.querySelectorAll('li');
			// 通过扩展运算符 将伪数组转成真正的数组
			console.log([...lis]);
			
			// Array.from() 还可以接收第二个参数,用来对每个元素进行处理
			let liContents = Array.from(lis,ele => ele.textContent);
			console.log(liContents);
			
			
			// 2.Array.of() 将任意的数据类型,转成数组
			console.log(Array.of(3,11,20,'30'));
			
			// 3.copyWithin() 数组内部将指定位置的元素复制到其他的位置,返回当前数组
			// 将索引为3的后面的全部数,替换成索引为0往后3位
			[1,2,3,8,9,10].copyWithin(0,3);//[8,9,10,8,9,10]
			
			// 4.find(n=>条件) findIndex(n=>条件)
			// find() 找出第一个符合条件的数组成员
			// findIndex() 找出第一个符合条件的数组成员的索引
			[1,2,-10,-20,9,2].find(n=>n<0);
			
			
			// 5.entries() keys() values() 返回一个遍历器,可以使用for...of循环进行遍历
			// keys() 对键名的遍历
			// values() 对值的遍历
			// entries() 对键值对的遍历
			
			// 遍历键
			for(let index of ['a','b'].keys()){
				console.log(index); // index 获取索引:0,1
			}
			// 遍历值
			for(let index of ['a','b'].values()){
				console.log(index); // index 获取值:a,b
			}
			// 遍历键值对
			for(let [index,ele] of ['a','b'].entries()){
				console.log(index,ele); // 0 "a" 1 "b"
			}
			
			// 数组的遍历器
			let letter = ['a','b','c'];
			let it = letter.entries();
			console.log(it.next().value);// 遍历第一次 [0,"a"]
			console.log(it.next().value);// 遍历第二次 [1,"b"]
			console.log(it.next().value);// 遍历第三次 [2,"c"]
			console.log(it.next().value);// 遍历第四次 undefined
			
			
			// 6. includes() 返回一个布尔值,表示某个数组是否包含给的值
			console.log([1,2,3].includes(2));// true 包含2
			console.log([1,2,3].includes('4'));// false 不包含'4'
			
			
			
		</script>
	</body>
</html>

迭代器Iterator

Iterator 是一种新的遍历机制

1.迭代器是一个接口,能快捷的访问数据,通过Symbol.iterator来创建迭代器,通过迭代器的next()获取迭代之后的接口

2.迭代器是用于遍历数据结构的指针

创建迭代器

数组[Symbol.iterator]() 方式创建迭代器

// 先创建一个数组
const item = ['one','two','three'];
// 1.数组或对象[Symbol.iterator]() 的方式创建迭代器
const ite = item[Symbol.iterator]();

使用迭代器

//迭代器.next() 获取迭代器的内容
//{value: 'one', done: false} done如果是true表示遍历完成,false表示遍历继续
// ite.next().value 获取对应的值
console.log(ite.next()); //第一次迭代,获取数组中的第一个元素 
console.log(ite.next()); //第二次迭代,获取数组中的第二个元素
console.log(ite.next()); //第三次迭代,获取数组中的第三个元素
console.log(ite.next()); //第四次迭代,超出数组的长度,undefined	

完整代码

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>迭代器Iterator</title>
	</head>
	<body>
		<script>
			// Iterator 是一种新的遍历机制
			// 迭代器是一个接口,能快捷的访问数据,通过Symbol.iterator来创建迭代器,通过迭代器的next()获取迭代之后的接口
			// 迭代器是用于遍历数据结构的指针
			
			// 使用迭代
			const item = ['one','two','three'];
			// console.log(item); // 可以查看迭代器中的方法
			// 数组[Symbol.iterator]() 的方式创建迭代器
			const ite = item[Symbol.iterator]();
			//迭代器.next() 获取迭代器的内容
			console.log(ite.next()); //第一次迭代,获取数组中的第一个元素
			// {value: 'one', done: false} done如果是true表示遍历完成,false表示遍历继续
			console.log(ite.next()); //第二次迭代,获取数组中的第二个元素
			console.log(ite.next()); //第三次迭代,获取数组中的第三个元素
			console.log(ite.next()); //第四次迭代,超出数组的长度,undefined
		</script>
	</body>
</html>

生成器Generator函数

generator函数 可以通过yield关键字,将函数挂起,为了改变执行流提供了可能性,同时为了做异步编程提供了方案

与普通函数的区别

1.function 后面 函数名之前有个*
2.只能在函数内部使用yield表达式,让函数挂起(停留在当前位置)

function* func(){yield xxx...;}

创建生成器函数

// 1.创建生成器函数
function* func(){
    console.log('one');
    yield 2; // 配合下面next()方法使用,第一次调用next()方法,程序执行会卡在这里
    console.log('two');
    yield 3;//配合下面next()方法使用,第二次调用next()方法,程序执行会卡在这里
    console.log('end'); //配合下面next()方法使用,第三次调用next()方法,程序执行会在这里结束
}

调用生成器函数

// 2.调用生成器函数,会返回一个遍历器对象,可以调用next()
let fn = func();
//console.log(fn); // func {<suspended>}对象
console.log(fn.next()); // Object:done:false value:2
console.log(fn.next()); // Object:done:true value:undefined
console.log(fn.next()); 

总结:generator函数是分段执行的,yield语句是暂停执行,而next()恢复执行

function* add(){
    console.log('start');
    // x 可真的不是yield '2'的返回值,它是next()调用恢复当前yield()执行传入的实参
    let x = yield '2';
    console.log('one:'+x);
    let y = yield '3';
    console.log('two:'+y);
    return x+y;
}
const fn1 = add();
console.log(fn1.next()); //  start; {value:'2',done:false}
console.log(fn1.next(20)); // 此时20赋值给了x   one:20; {value:'3',done:false}
console.log(fn1.next(30)); // 此时30赋值给了y  two:30; {value:50,done:true}

生成器的使用场景

// 使用场景1: 为不具备Interator接口对象提供了遍历操作
function* objectEntries(obj){
    // 获取对象的所有的key保存到数组 [name,age]
    const propKeys = Object.keys(obj);
    for(const propKey of propKeys){
        yield [propKey,obj[propKey]];
    }
}
// 此时obj不能遍历
const obj = {
    name:'小马哥',
    age:18
}
// 给obj对象赋予了迭代器
obj[Symbol.iterator] = objectEntries;

// 此时obj就可以遍历了
for(let [key,value] of objectEntries(obj)){
    console.log(`${key},${value}`);
}

完整代码

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>生成器Generator函数</title>
	</head>
	<body>
		<script>
			// generator函数 可以通过yield关键字,将函数挂起,为了改变执行流提供了可能性,同时为了做异步编程提供了方案
			// 与普通函数的区别
			// 1.function 后面 函数名之前有个*
			// 2.只能在函数内部使用yield表达式,让函数挂起(停留在当前位置)
			
			// 1.创建生成器函数
			function* func(){
				console.log('one');
				yield 2; // 配合下面next()方法使用,第一次调用next()方法,程序执行会卡在这里
				console.log('two');
				yield 3;//配合下面next()方法使用,第二次调用next()方法,程序执行会卡在这里
				console.log('end'); //配合下面next()方法使用,第三次调用next()方法,程序执行会在这里结束
			}
			// 2.调用生成器函数,会返回一个遍历器对象,可以调用next()
			let fn = func();
			//console.log(fn); // func {<suspended>}对象
			console.log(fn.next()); // Object:done:false value:2
			console.log(fn.next()); // Object:done:true value:undefined
			console.log(fn.next()); 
			
			
			// 总结:generator函数是分段执行的,yield语句是暂停执行,而next()恢复执行
			
			
			function* add(){
				console.log('start');
				// x 可真的不是yield '2'的返回值,它是next()调用恢复当前yield()执行传入的实参
				let x = yield '2';
				console.log('one:'+x);
				let y = yield '3';
				console.log('two:'+y);
				return x+y;
			}
			const fn1 = add();
			console.log(fn1.next()); //  start; {value:'2',done:false}
			console.log(fn1.next(20)); // 此时20赋值给了x   one:20; {value:'3',done:false}
			console.log(fn1.next(30)); // 此时30赋值给了y  two:30; {value:50,done:true}
			
			
			// 使用场景 为不具备Interator接口对象提供了遍历操作
			function* objectEntries(obj){
				// 获取对象的所有的key保存到数组 [name,age]
				const propKeys = Object.keys(obj);
				for(const propKey of propKeys){
					yield [propKey,obj[propKey]];
				}
			}
			// 此时obj不能遍历
			const obj = {
				name:'小马哥',
				age:18
			}
			obj[Symbol.iterator] = objectEntries;
			
			// 此时obj就可以遍历了
			for(let [key,value] of objectEntries(obj)){
				console.log(`${key},${value}`);
			}
		</script>
	</body>
</html>

生成器的应用

回调地狱函数

多次发送请求,也就是在ajax中再发ajax请求中再发ajax,Generator 部署ajax操作,这种情况 回调地狱 此时就可以用生成器解决,让异步代码同步化

场景复现

// Generator 部署ajax操作,让异步代码同步化
// 多次发送请求,也就是在ajax中再发ajax请求中再发ajax
// 这种情况 回调地狱 此时就可以用生成器解决
$.ajax({
    url:'https://devapi.qweather.com/v7/weather/now?location=101010100&key=be57c5ee9a2c462f8bb517efd86343fc',
    method:'get',
    success(res){
        console.log(res);
        // 继续发送请求
        $.ajax({
            url:'',
            method:'get',
            success(res1){
                // 再次发送ajax请求
                $.ajax({
                    url:'',
                    method:'get',
                    success(res2){
                        // 还要发送请求
                    }
                })
            }
        })
    }
})

解决地狱回调函数

用生成器解决 多次回调函数 异步代码同步化

// 创建生成器对象
function* main(){
    let res = yield request('https://devapi.qweather.com/v7/weather/now?location=101010100&key=be57c5ee9a2c462f8bb517efd86343fc')
    console.log(res);
    //执行后面的操作
    console.log('数据请求完成,可以继续操作');
}
// 创建迭代器对象
const ite = main();
ite.next();
function request(url){
    $.ajax({
        url:url,
        method:'get',
        success(res){
            ite.next(res);
        }
    })
}
加载页面

加载loading…页面
数据加载完成…(异步操作)
loading关闭掉

// 应用2:加载loading...页面
// 数据加载完成...(异步操作)
// loading关闭掉

function load1(){
    // !!!!!!!!!!!!!!!!!!!!!!!!!!读者看此案例的时候,先看这里注释说明!!!!!!!!!!!!!!!
    // 如果是这种方式调用就会出现问题
    // 原本的结果应该是 加载页面,数据加载完成,加载页面关闭
    // 但是由于网络延迟的问题(本案例使用了定时器模拟了网络延迟),实际上的结果是 加载页面,加载页面关闭,数据加载完成
    // 这显然是不合理的,所以这种方式调用函数不可行,要用生成器的方式,让异步代码同步化才能解决该问题
    loadUI();
    showData();
    hideUI();
}

// 创建生成器函数
function* load(){
    loadUI();
    yield showData();// 配合下面的 itLoad.next() 让程序卡在这里,等该程序执行完,再执行下一行代码
    hideUI();
}
let itLoad = load();
// 第一次调用会显示加载ui界面,并且异步的加载数据
itLoad.next(); //**


function loadUI(){
    console.log('加载loading...页面');
}
function showData(){
    setTimeout(() => {
        console.log('数据加载完成.....');
        //第二调用,会调用hideUI(),隐藏Loading
        itLoad.next();//***
    }, 1000);

}
function hideUI(){
    console.log('隐藏loading...页面');
}

完成代码

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Generator的应用</title>
		<!-- 引入在线的jquery -->
		<script src="https://code.jquery.com/jquery-3.7.1.js"></script>
	</head>
	<body>
		<script>
			// Generator 部署ajax操作,让异步代码同步化
			// 多次发送请求,也就是在ajax中再发ajax请求中再发ajax
			// 这种情况 回调地狱 此时就可以用生成器解决
			$.ajax({
				url:'https://devapi.qweather.com/v7/weather/now?location=101010100&key=be57c5ee9a2c462f8bb517efd86343fc',
				method:'get',
				success(res){
					console.log(res);
					// 继续发送请求
					$.ajax({
						url:'',
						method:'get',
						success(res1){
							// 再次发送ajax请求
							$.ajax({
								url:'',
								method:'get',
								success(res2){
									
								}
							})
						}
					})
				}
			})
			
			// 用生成器解决 多次回调函数 异步代码同步化
			
			// 创建生成器对象
			function* main(){
				let res = yield request('https://devapi.qweather.com/v7/weather/now?location=101010100&key=be57c5ee9a2c462f8bb517efd86343fc')
			console.log(res);
			//执行后面的操作
			console.log('数据请求完成,可以继续操作');
			}
			// 创建迭代器对象
			const ite = main();
			ite.next();
			function request(url){
				$.ajax({
					url:url,
					method:'get',
					success(res){
						ite.next(res);
					}
				})
			}
			
			// 应用2:加载loading...页面
			// 数据加载完成...(异步操作)
			// loading关闭掉
			
			function* load(){
				loadUI();
				yield showData();// 配合下面的 itLoad.next() 让程序卡在这里,等该程序执行完,再执行下一行代码
				hideUI();
			}
			let itLoad = load();
			// 第一次调用会显示加载ui界面,并且异步的加载数据
			itLoad.next(); //****************核心调用代码***************
			
			
			function loadUI(){
				console.log('加载loading...页面');
			}
			function showData(){
				setTimeout(() => {
					console.log('数据加载完成.....');
					//第二调用,会调用hideUI(),隐藏Loading
					itLoad.next();//****************核心调用代码***************
				}, 1000);
				
			}
			function hideUI(){
				console.log('隐藏loading...页面');
			}		
		</script>
	</body>
</html>

Promise对象

Promise的基本使用

Promise相当于一个容器,保存着未来才会结束的事件(异步操作)的一个结果
各种异步操作都可以用同样的方法进行处理

Promise特点:

1.对象的状态不受外界影响 处理异步操作 三个状态 Pending(进行) Resolved(成功) Rejected(失败)
2.一旦状态改变,就不会再变,任何时候都可以得到这个结果

1.创建Promise对象

let pro = new Promise(function(resolved,rejected){
    // 接收返回成功的数据
    res = {} // 接收的数据
    if(){
        // 接收请求成功返回的数据
       resolved(res);
    }else{
        // 接收请求失败的错误信息
        rejected(res)
    }   
})

2.成功或失败处理数据

pro.then((val)=>{
    // 此时val就是成功接收的数据
    // 返回成功数据之后要做的事情
},(err)=>{
    // 返回失败信息之后要做的事情
})

完整代码

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Promise的使用</title>
	</head>
	<body>
		<script>
			// Promise 承诺
			// 相当于一个容器,保存着未来才会结束的事件(异步操作)的一个结果
			// 各种异步操作都可以用同样的方法进行处理
			// 特点
			// 1.对象的状态不受外界影响 处理异步操作 三个状态 Pending(进行) Resolved(成功) Rejected(失败)
			// 2.一旦状态改变,就不会再变,任何时候都可以得到这个结果
			
			// 创建Promise对象
			let pro = new Promise(function(resolved,rejected){
				// 执行异步操作
				// 假设res是后端传来的内容
				let res = {
					code:200,
					data:{
						name:'小马哥'
					},
					error:"失败了"
				}
				// 1.模拟网络延迟一秒之后,接收到后端传来的res内容
				setTimeout(()=>{
					// 模拟接收成功
					if(res.code === 200){
						resolved(res.data);
					}else{
						// 模拟接收失败
						rejected(res.error);
					}
				},1000);
				
			})
			// console.log(pro);
			
			// 2.接收到了后端传来的数据,后续要进行的操作,成功归成功的操作,失败归失败之后的操作
			// Promise对象.then(()=>{})接收异步程序执行成功和失败的结果
			pro.then((val)=>{
				// 此时val是后端传进来的数据
				console.log(val);
			},(err)=>{
				console.log(err);
			});
			
			
			// 上述代码太繁琐,下面代码更加简便
			
			function timeOut(ms){
				return new Promise((resolved,rejected)=>{
					setTimeout(()=>{
						resolved('hello promise success!!')
					},ms)
				})
			}
			
			timeOut(2000).then((val)=>{
				// 此时val是返回来的结果
				console.log(val);
			})
			
		</script>
	</body>
</html>

使用Promise封装ajax

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>使用promise封装ajax</title>
	</head>
	<body>
		<script>
			const getJSON = function(url){
				return new Promise((resolve,reject)=>{
                    // 1.创建XMLHttpRequest对象
					const xhr = new XMLHttpRequest();
                    // 2.设置请求方式
					xhr.open('GET',url); //GET一定要大写
                    
					// 3.xhr.onreadystatechange 请求的状态 绑定事件函数
					xhr.onreadystatechange = handler;
					// 4.设置返回数据的类型为json
					xhr.responseType = 'json';
					// 5.设置请求头
					xhr.setRequestHeader('Accept','application/json');
					// 6.发送
					// 如果发送的是POST的请求,请求参数写在send()括号里
					xhr.send();
                    
                    // 事件函数
					function handler(){
						// console.log(this);
						// 如果请求状态为4 表示请求完成
						if(this.readyState === 4){
							// 如果请求成功
							if(this.status === 200){
								resolve(this.response);
							}else{
                                // 请求失败
								reject(new Error(this.statusText));
							}
						}
					}
					
				})
			}
			
			
			getJSON('https://devapi.qweather.com/v7/weather/now?location=101010100&key=be57c5ee9a2c462f8bb517efd86343fc').then((data)=>{
				console.log(data);
			},(error)=>{
				console.log(error);
			})
		</script>
	</body>
</html>

Promise对象中的方法

then()方法

then() 第一个参数是relove回调函数,第二个参数是可选的,是reject状态回调的函数

then()方法返回一个新的promise实例,可以采用链式写法

其中 .then(null,err=>{}) 等价于 .catch(err=>{})

// 一般情况下,这么写
getJSON('https://devapi.qweather.com/v7/weather/now?location=101010100&key=be57c5ee9a2c462f8bb517efd86343fc').then((data)=>{
    console.log(data);
}).catch(err=>{
    console.log(err);
})

const getJSON = function(url){
    return new Promise((resolve,reject)=>{
        const xhr = new XMLHttpRequest();
        xhr.open('GET',url); //GET一定要大写
        // xhr.onreadystatechange 请求的状态
        xhr.onreadystatechange = handler;
        // 设置返回数据的类型为json
        xhr.responseType = 'json';
        // 设置请求头
        xhr.setRequestHeader('Accept','application/json');
        // 发送
        // 如果发送的是POST的请求,请求参数写在send()括号里
        xhr.send();
        function handler(){
            // console.log(this);
            // 如果请求状态为4 表示请求完成
            if(this.readyState === 4){
                // 如果请求成功
                if(this.status === 200){
                    resolve(this.response);
                }else{
                    reject(new Error(this.statusText));
                }
            }
        }

    })
}
resolve()方法

能将现有的任何对象转换成promise对象

// resolve() 能将现有的任何对象转换成promise对象
let p = Promise.resolve('foo');
// let p = Promise.resolve('foo') 等价于 let p = new Promise(resolve=>resolve('foo'))
console.log(p);
p.then((data)=>{
    console.log(data);
})
all()方法

让所有异步程序并行执行

应用:一些游戏嘞的素材比较多,等待图片,flash,静态资源文件都加载完成,然后才进行页面初始化

// all() 让所有异步程序并行执行
// 应用:一些游戏嘞的素材比较多,等待图片,flash,静态资源文件都加载完成,然后才进行页面初始化
let promise1 = new Promise((resolve,reject)=>{});
let promise2 = new Promise((resolve,reject)=>{});
let promise3 = new Promise((resolve,reject)=>{});
let p4 = Promise.all([promise1,promise2,promise3]);

p4.then(()=>{
    // 三个异步都成功,才成功
}).catch((err)=>{
    // 如果有一个失败 则失败
})
race()方法

某个异步请求设置超时事件,并且在超时后执行相应的操作

// race() 某个异步请求设置超时事件,并且在超时后执行相应的操作
function requestImg(imgSrc){
    return new Promise((resolve,reject)=>{
        const img = new Image();
        img.onload = function(){
            resolve(img);
        }
        img.src = imgSrc;
    });
}
function timeout(){
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            reject(new Error(''图片请求超时''));
        },3000);
    })
}
// 当请求images/2.png时,在三秒之内能请求下来,就执行.then方法,如果三秒之后就会走catch
Promise.race([requestImg('images/2.png'),timeout()]).then(res=>{
    //三秒内请求成功
    console.log(res);
}).catch(err=>{
    // 请求超过
    console.log(err);
})
done()和finally()方法

.done() 和 .finally() 方法在不管请求失败还是成功,都会执行

Promise.race([requestImg('images/2.png'),timeout()]).then(res=>{
//三秒内请求成功
console.log(res);
}).catch(err=>{
// 请求超过
console.log(err);
}).done()或者.finally()

async异步操作

async 让异步操作更加方便,非常推荐使用

用async标识的函数,使函数有异步行为

async会返回一个Promise对象,可以使用 then catch方法,async其实是Generator的一个语法糖

async的基本使用

// 定义async
/* 
fn函数被async修饰后,该函数返回值就是一个promise对象,再配合await使用,将函数内部的代码转成promise对象
使得普通函数,也拥有了promise的特性,也可以执行then,catch等promise中的方法
而且由于函数内部的代码被await修饰,所以函数内部的每一行代码都得等上一行代码执行完,才会往下执行
*/
async function fn(){
    await 代码部分
}
fn().then(res=>{/*请求成功的代码*/}).catch(erro=>{/*请求失败的代码*/})

async的成功回调

async function f(){
    // await 等待 一定要在async中使用 会将被await修饰的对象转换成Promise对象
    // return await 'hello async';
    let s = await 'hello world';
    let data = await s.split(''); // 上述代码执行完了,这行代码才会执行
    return data;//上面的代码都执行了完了,才会执行这行代码
}
//console.log(f());// 打印出Promise对象
// 如果async函数中有多个await,那么then函数会等待所有的await执行运行完的结果,才去执行
f().then(v=>{console.log(v)}).catch(e=>console.log(e));

async的失败回调

async function f2(){
	// throw new Error('出错了');;
	await Promise.reject('出错了');//请求失败,代码就会卡在这里
	await Promise.resolve('hello');//由于上面的请求失败,所以该行代码不会执行
}
f2().then(v=>console.log(v)).catch(e=>console.log(e));//输出结果:出错了

但是上述方法,会产生问题,就是说,请求失败,代码就会卡在失败的位置,不会继续执行下去,所以有了下面的解决方案

async function f2(){
    // throw new Error('出错了');;
    // 解决方案
    try{
        await Promise.reject('出错了');
    }catch(error){

	}
	// 这样上面的代码无论请求失败还是成功,下面的代码都会执行
	return await Promise.resolve('hello');
}

f2().then(v=>console.log(v)).catch(e=>console.log(e));//输出结果:hello

async应用案例

这里以和风天气接口为案例

// 案例
// 需求:想获取和风天气,现在now的数据

// getJSON 方法封装了ajax请求
const getJSON = function(url){
    return new Promise((resolve,reject)=>{
        // 1.创建XMLHttpRequest对象
        const xhr = new XMLHttpRequest();
        // 2.设置请求方式
        xhr.open('GET',url); //GET一定要大写

        // 3.xhr.onreadystatechange 请求的状态 绑定事件函数
        xhr.onreadystatechange = handler;
        // 4.设置返回数据的类型为json
        xhr.responseType = 'json';
        // 5.设置请求头
        xhr.setRequestHeader('Accept','application/json');
        // 6.发送
        // 如果发送的是POST的请求,请求参数写在send()括号里
        xhr.send();

        // 事件函数
        function handler(){
            // console.log(this);
            // 如果请求状态为4 表示请求完成
            if(this.readyState === 4){
                // 如果请求成功
                if(this.status === 200){
                    resolve(this.response);
                }else{
                    // 请求失败
                    reject(new Error(this.statusText));
                }
            }
        }

    })
}


async function getNowWeather(url){
    // 发送ajax 可以获取实况天气
    let res = await getJSON(url);
    console.log(res);
    // 获取和风天气返回的数据 可以获取未来3到7的天气
    let arr = await res;
    return arr[0].now;
}
getNowWeather('https://devapi.qweather.com/v7/weather/now?location=101010100&key=be57c5ee9a2c462f8bb517efd86343fc').then(now=>{
    console.log(now);
})

总结

Generator Promise async的出现,解决了回调地狱的问题,使得异步操作显得更加的方便

class类的用法

使用模板

class 类名{
    // constructor 原型上内置方法 所以是固定写法
	constructor(形参1,形参2,...){
        this.形参1 = 形参1;
        this.形参2 = 形参2;
        ...
    }
    // 赋值方法
    方法1(){
        // 代码
        return 返回值
    }
    方法2(){
        // 代码
        return 返回值
    }
}
// 调用类
let 变量 = 类名(实参1,实参2);
变量.方法1();
变量.方法2();
....

具体使用

class Person{
    //constructor 原型上内置方法
    // constructor 实例化的时候会立即被调用
    constructor(name,age){
        this.name = name;
        this.age = age;
    }
    // 赋值方法
    sayName(){
        return this.name;
    }
    sayAge(){
        return this.age;
    }
}

let p1 = new Person('小马哥',28);
console.log(p1);
p1.sayName()// '小马哥'
p1.sayAge()//28

一次性添加多个方法

不常用

class Person1{
    //constructor 原型上内置方法
    // constructor 实例化的时候会立即被调用
    constructor(name,age){
        this.name = name;
        this.age = age;
    }
}

// 通过Object.assign() 方法一次性向类中添加多个方法
Object.assign(Person.prototype,{
    sayName(){
        return this.name;
    },
    sayAge(){
        return this.age;
    }
})
let p2 = new Person1('小马哥',28);
console.log(p2);
p2.sayName()// '小马哥'
p2.sayAge()//28

类的继承

用extends来继承父类

class 子类 extends 父类 的形式就可以子类继承父类

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>类的继承</title>
	</head>
	<body>
		<script>
			// 使用关键词 extends
			// 父类
			class Animal{
				constructor(name,age){
					this.name = name;
					this.age = age;
				}
				sayName(){
					return this.name;
				}
				sayAge(){
					return this.age;
				}
			}
			
			
			// 子类
			// 子类 extends 父类 的形式 就可以子类继承父类
			class Dog extends Animal{
				constructor(name,age,color){
					// super() 会调用父类中的方法
					super(name,age); // 将父类中的this.name = name;this.age = age; 都可继承过来了
					this.color = color;
				}
				// 子类自己的方法
				sayColor(){
					return `${this.name}${this.age}岁了,它的颜色是${this.color}`
				}
				// 重写父类的方法
				sayName(){
					return this.name + super.sayAge() + this.color;
				}
				
				
			}
			
			let d1 = new Dog('小黄',28,'red');
			console.log(d1);
			d1.sayAge(); // 来自于父类的方法
			d1.sayName(); // 来自于子类重写父类的方法
			
		</script>
	</body>
</html>

ES6中的模块化实现

ES6中的module是服务器和浏览器通用的模块化

案例准备

首先,先准备一个外部文件,名字就叫:ES6的模块化实现.html

其次,创建一个文件夹,名为modules,再在该文件夹创建一个index.js文件

使用es6的模块化方式

1.index.js外部js文件中定义好变量或者函数或者类,然后用export关键字给抛出来

2.在ES6的模块化实现.html文件中,使用<script type="module"></script> 标签包裹,记住,一定要带 type=“module”

3.使用 import 外部js文件中的export default 对象, {外部js文件的变量或函数名} from '外部js文件的路径' 来引入外部js文件(index.js)中的变量,函数,或者类

使用模块化

在HTML文件中包裹标签

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>ES6的模块化</title>
	</head>
	<body>
		<!-- 想要引入外部模块,script标签必须要写type="module" -->
		<script type="module">
            
		</script>
	</body>
</html>

外部js文件抛出对象

index.js抛出对象

// 抛出方式1 (非常推荐使用)
export const 变量 = '值';
export function 函数(){};
export 对象 = {'键':'值'};
export class{constructor(){}类中的方法(){}}
// 抛出方式2
const 变量 = '值';
function 函数(){};
class{constructor(){}类中的方法(){}}
export{
	变量,函数,对象,}
// 抛出方式3 
// 注意:一个js文件只能有一个export default
// const 变量 或者 函数 或者 对象 或者 类;
// 这里以对象为例
const obj = {foo:'foo'}
// 使用 export default 方式抛出
// 注意:一个js文件只能有一个export default
export default obj

主文件引入对象

ES6的模块化实现.html 文件引入对象

// 引入方式1
// import 外部js文件中的export default对象,{外部js文件的变量或函数名} from '外部js文件的路径'
// import 【export default修饰的对象】,{变量,函数,类,} from './modules/index.js';
import obj,{name,age,sayName,Person} from './modules/index.js';
// 此时obj是index.js文件中的export default obj;
// 引入方式2
// 一次性将外部js文件中的变量或者函数全部抛出
import * as f from './modules/index.js';
// 上述代码解释:取出外部js文件中的所有对象,并且名为为f,用f.变量来取值

完整代码

ES6的模块化实现.html文件中的内容

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>ES6的模块化</title>
	</head>
	<body>
		<!-- 想要引入外部模块,script标签必须要写type="module" -->
		<script type="module">
			// ES6中的module是服务器和浏览器通用的模块化
			// import {外部js文件的变量或函数名} from '外部js文件的路径'
			import obj,{name,age,sayName,Person} from './modules/index.js';
			
			// 一次性将外部js文件中的变量或者函数全部抛出
			// import * as f from './modules/index.js';
			// 取出外部js文件中的所有对象,并且名为为f,用f.变量来取值
			
			// 此时obj是index.js文件中的export default obj;
			
			console.log(name,age,sayName);
			
		</script>
	</body>
</html>

./modules/index.js 文件中的内容

// es6的模块功能主要有两个命令构成:export 和 import

// export 用于规定模块的对外接口
// import 用于输入其他模块提供的功能
// 一个模块就是一个独立的文件

export const name = '张三';
export const age = 18;
export function sayName(){
	return 'my name is aaa';
}

// 一次性抛出多个变量
const name1 = '张三';
const age1 = 18;
function sayName1(){
	return 'my name is aaa';
}
export{
	name1,age1,sayName1
}


const obj = {
	foo:'foo'
}

// 注意:一个js文件只能有一个export default
export default obj;


export class Person{
	constructor(){
		
	}
	sayAge(){
		console.log('16');
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值