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');
}
}