一、ES介绍
1.1 什么是ECAMScript
ES全称ECMAScript,是脚本语言的规范,而平时经常编写的JavaScript,是EcmaScript的一种实现,所以ES新特性其实指的就是JavaScript的新特性。
ECMAScript是由Ecma国际通过ECMA-262标准化的脚本程序设计语言。
1.2 什么是ECMA-262
Ecma国际制定了许多标准,而ECMA-262只是其中的一个,所有的标准列表查看
http://www.ecma-international.org/publications/standards/Standard.htm
1.3 ECMA-262历史
http://www.ecma-international.org/publications/standards/Ecma-262-arch.htm
注:从ES6开始,每年发布一个版本,版本号比年份最后一位大1
1.4 谁在维护ECMA-262
TC39(Technical Committee 39)是推进ECMAScript发展的委员会。其会员都是公司(其中主要是浏览器厂商,有苹果,谷歌,微软,因特尔等)。TC39定期召开会议,会议由会员公司的代表与特邀专家出席。
1.5 为什么要学习ES6
- ES6的版本变动内容最多。具有里程碑意义
- ES6加入许多新的语法特性,编程实现更简单、高效
- ES6是前端发展趋势
1.6 ES6兼容性
http://kangax.github.io/compat-table/es6/ 可查看兼容性
二、ES6-let变量声明
1、let变量声明的格式
与 var 声明变量格式一样
2、let声明变量的特性
(1)变量不能重复声明
let star = '罗志祥';
let star = '小猪';
(2)块级作用域,代码只在代码块内有效
{
let girl = '周扬青';
}
console.log(girl); //控制台显示 girl is not defined
(3)不存在变量提升(变量在声明之前,不能使用该变量)
console.log(song);
let song = '稻香'; //控制台显示 Cannot access 'song' before initialization
(4)不影响作用域链
{
let school = '清华大学';
function fn() {
console.log(school);
}
fn();
}
3、let件经典案例
let item = document.querySelectorAll('.item');
//1、常规var用法
for (var i = 0; i < item.length; i++) {
item[i].addEventListener('click', function() {
this.style.background = 'pink';
})
}
//2、使用let变量声明用法
for (let i = 0; i < item.length; i++) {
item[i].addEventListener('click', function() {
item[i].style.background = 'pink';
})
}
三、ES6-conset声明常量
1、conset声明常量格式
常量:值不能修改的量
与var let 一样
2、注意事项
(1)一定要赋初始值
const A; //报错
(2)一般常量使用大写(潜规则)
const a = 1000;
(3)常量的值不能修改
const SCHOOL = '清华大学';
SCHOOL = 'ATM';
(4)块级作用域
{
const PLAYER = 'UZI';
}
console.log(PLAYER); //PLAYER is not defined
(5)对于数组和对象的元素修改,不算做对常量的修改,不会报错
const TEAM = ['UZI', 'MXLG', 'Ming', 'Letme'];
TEAM.push('Meiko');
四、ES6变量的解构赋值
ES6允许按照一定模式从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。
1、数组的解构
const F4 = ['貂蝉', '蔡文姬', '虞姬', '甄姬'];
let [diao, cai, yu, zhen] = F4;
console.log(diao); //貂蝉
console.log(cai); //蔡文姬
console.log(yu); //虞姬
console.log(zhen); //甄姬
2、对象的解构
const diao = {
name: '貂蝉',
age: '不详',
zhandou: function() {
console.log('我可以五连绝世');
}
};
let {zhandou} = diao;
zhandou(); //我可以五连绝世
五、ES6模板字符串
ES6引入新的声明字符串的方式 :[``] 、 ' ' 、 " "。
1、声明
let str = `我也是一个字符串哦`;
console.log(str, typeof str);
2、特性
① 内容中可以直接出现换行符
let str = `<ul>
<li>王者荣耀</li>
<li>金铲铲之站</li>
<li>原神</li>
</ul>`;
② 可以直接进行变量拼装
let lovest = '林宥嘉';
let out = `${lovest}是我心目中最棒的歌手`;
console.log(out); //林宥嘉是我心目中最棒的歌手
六、对象的简化写法
ES6允许在大括号里面,直接写入变量和函数,作为对象的属性和方法
这样的书写更加简洁
let name = '甄姬';
let change = function() {
console.log('阿宓会带来不幸');
}
const school = {
name,
change,
improve() {
console.log("我们可以提高你的技能");
}
}
console.log(school);
七、箭头函数以及声明特点
ES6允许使用箭头(=>)定义函数
1、箭头函数语法结构
//1、常规写法
let fn = function() {
}
//2、使用箭头函数
let fn = () => {
}
//实例
let fn = (a, b) => {
return a + b;
}
let result = fn(1, 2);
console.log(result); //3
2、特点
2.1 this是静态的,this始终指向函数声明时所在的作用域下的this的值
function getName () {
console.log(this.name);
}
let getName2 = () >= {
console.log(this.name);
}
//设置window对象的name属性
window.name = '吃什么';
const food = {
name: "西瓜",
}
2.2 箭头函数不能作为构造函数去实例化对象
let Person = (name, age) => {
this.name = name;
this, age = age;
}
let me = new Person('xiao', 30);
console.log(me); // Person is not a constructor
2.3 箭头函数不能使用arguments变量
let fn = () => {
console.log(arguments);
}
fn(1, 2, 3); //arguments is not defined
3、箭头函数的简写
3.1 当形参有且只有一个的时候,可省略小括号
//1、原始写法
let add = (n) => {
return n+n;
}
console.log (add(9));
//2、简写
let add = n => {
return n+n;
}
console.log (add(9)); //18
3.2 当代码体只有一条语句的时候,此时return必须省略,语句的执行结果就是函数的返回值,此时省略花括号
//1、原始写法
let pow = (n) => {
return n*n;
}
console.log(pow(5));
//2、简写
let pow = n => n*n;
console.log(pow(5)); //25
4、实践与应用
4.1 点击div 2s 后颜色变为天蓝色
<div class=“ad”></div>
//1、原始写法
let ad = document.querySelectior('.ad');
ad.addEventListener('click',function(){
// 保存this的值 因为此时定时器的初始写法为window.setTimeout(),需要特殊声明
let _this = this;
//定时器
setTimeout(function(){
_this.style.background = 'skyblue';
}.2000);
});
//2、简写
let ad = document.querySelectior('.ad');
ad.addEventListener('click',function(){
setTimeout(() => {
//此时的this指向的是声明函数下的this ad
this.style.background = 'skyblue';
}.2000);
});
4.2 从数组中返回偶数元素
const arr = [1, 6, 9, 10, 100, 25];
//1、原始写法
const result = arr.filter(function(item) {
if(item % 2 === 0 ) {
return true;
}else {
return false;
}
})
console.log(result);
//2、简写
const result = arr.filter (item => item % 2 === 0);
console.log(result); //Array(3)
5、总结
箭头函数适合于this无关的回调,如:定时器,数组的方法回调。
箭头函数不适合与this 有关的回调,如:dom事件回调,对象的方法。
八、函数参数默认值
ES6允许给函数参数(形参)赋值初始值
1、形参初始值,具有默认的参数,一般位置要靠后
function add(a, b, c = 10) {
return a + b + c;
}
let result = add(1, 2);
console.log(result); //1+2+10=13
2、可与解构赋值相结合
function connect ({host,username,password,port}) {
console.log(host);
console.log(username);
console.log(password);
console.log(port);
}
connect ({
host: 'localhost';
username;'root';
password: 'root';
port: 3306
})
3、rest参数
ES6 引入rest参数,用于获取函数的实参,用来代替arguments。
rest参数必须要放到参数最后面
function date(...args) {
console.log(args);
}
date('白芷', '陈皮', '决明子'); // ['白芷', '陈皮', '决明子']
function fn(a, b, ...args) {
console.log(a); //1
console.log(b); //2
console.log(args); //[3, 4, 5, 6]
}
fn(1, 2, 3, 4, 5, 6);
九、扩展运算符
1、语法结构
【...】 扩展运算符能够将数组转行为逗号分隔的参数序列
const tyboys = ['易烊千玺', '王源', '王俊凯']; // => '易烊千玺', '王源', '王俊凯'
function chunwan (){
console.log(arguments);
}
chunwan(...tfboys); //等同于 chunwan('易烊千玺', '王源', '王俊凯')
2、扩展运算符的运用
2.1 数组的合并
const kuaizi = ['王太利', '肖央'];
const fenghuang = ['曾毅', '玲花'];
const zuixuanxiaopingguo = [...kuaizi,...fenghuang];
console.log(zuixuanxiaopingguo);
2.2 数组的克隆(引用类型数据,浅拷贝)
const sanzhihua = ['E','G','M'];
const sanyecao = [...sanzhihua];
console.log(sanyecao);
2.3 将伪数组转为真正的数组
const divs = document.querySelectorAll('div');
const divArr = [...divs];
console.log(divArr);
十、Symbol
1、Symbol的基本使用
ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。它是JavaScript语言的第七种数据类型,是一种类似于字符串的数据类型。
拓展:JavaScript语言的第七种数据类型 USONB
U: undefined
S: string symbol
O: object
N: null number
B: boolean
1.1 Symbol的特点
(1)Symbol的值是唯一的,用来解决命名冲突的问题
let s = Symbol();
console.log(s, typeof s);
let s2 = Symbol('王者荣耀');
let s3 = Symbol('王者荣耀');
console.log(s2 === s3); /* false */
(2)Symbol值不能与其他数据进行运算
(3)Symbol定义的对象属性不能使用for...in循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名。
let s4 = Symbol.for('王者荣耀');
let s5 = Symbol.for('王者荣耀');
console.log(s4 === s5); /* true */
1.2 Symbol创建对象属性
let game = {
name: '俄罗斯方块',
up: function() {},
down: function() {}
};
//声明一个对象
let methods = {
up : Symbol(),
down : Symbol()
};
game[methods.up] = function(){
console.log('我可以改变形状');
}
game[methods.down] = function(){
console.log('我可以快速下降');
}
console.log(game); //{name: '俄罗斯方块', up: ƒ, down: ƒ, Symbol(): ƒ, Symbol(): ƒ}
let youxi = {
name:'狼人杀',
[Symbol('say')]:function(){
console.log('我可以发言');
},
[Symbol('zibao')]:function(){
console.log('我可以自爆');
}
};
console.log(youxi); //{name: '狼人杀', Symbol(say): ƒ, Symbol(zibao): ƒ}
2、Symbol的内置值
除了定义自己使用的Symbol值以外,ES6还提供了11个内置的Symbol值,只想语言内部使用的方法。
Symbol.hasInstance | 当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法 |
Symbol.isConcatSpeadble | 对象的Symbol.isConcatSpreadable属性等于的是一个布尔值,表示该对象用于Array.prototype,concat()时,是否可以展开。 |
Symbol.unscopables | 该对象指定了使用with关键字时,哪些属性会被with环境排除。 |
Symbol.match | 当执行str.match(myObject)时,如果该属性存在,会调用它,返回该方法的返回值。 |
Symbol.replace | 当该对象被str.replace(myObject)方法调用时,会返回该方法的返回值。 |
Symbol.search | 当该对象被str.search(myObject)方法调用时,会返回该方法的返回值。 |
Symbol.split | 当该对象被str.split(myObject)方法调用时,会返回该方法的返回值。 |
Symbol.iterator | 对象进行for...of循环时,会调用Symbol.iterator方法,返回该对象的默认遍历器。 |
Symbol.toPrimitive | 该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。 |
Symbol.toStringTag | 在该对象上面调用toString方法时,返回该方法的返回值 |
Symbol.species | 创建衍生对象时,会使用该属性。 |
//1、Symbol.hasInstance
class Person {
static[Symbol.hasInstance]() {
console.log('我被用来检测类型了');
}
}
let o = {};
console.log(o instanceof Person); //false
//2、Symbol.isConcatSpeadble
const arr = [1, 2];
const arr2 = [3, 4, 5];
arr2[Symbol.isConcatSpreadable] = false; //如果设置为false,则代表数组不可展开
console.log(arr.concat(arr2)); //[1, 2, Array(3)]
十一、迭代器
迭代器(Iterator)是一种接口,为各种不同的数据结构提供同意的访问机制。任何数据结构只要部署iterator接口,就可以完成遍历操作。
(1)ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费
(2)原生具备iterator接口的数据(可用for of遍历)
Array Arguments Set Map String TypeArray NodeList
(3)工作原理:
① 创建一个指针对象,指向当前数据结构的起始位置
② 第一次调用对象的next方法,指针自动指向数据结构的第一个成员
③ 接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员
④ 每调用next方法返回一个包含value和done属性的对象
注:需要自定义遍历数据的时候,要想到迭代器。
const xiyou = ['唐僧','孙悟空','猪八戒','沙僧'];
//使用for..of 遍历
//for...of 保存的是键值(遍历数组) for...in 保存的是键名(遍历对象、数组)
/*for (let v of xiyou){
console.log(v);
}*/
let iterator = xiyou[Symbol.iterator]();
//调用对象的next方法
console.log(iterator.next()); //{value: '唐僧', done: false}
console.log(iterator.next()); //{value: '孙悟空', done: false}
console.log(iterator.next()); //{value: '猪八戒', done: false}
console.log(iterator.next()); //{value: '沙僧', done: false}
console.log(iterator.next()); //{value: undefined, done: true}
//迭代器应用-自定义遍历数组
const banji = {
name:"终极一班",
stus: [
'xiaoming',
'xiaoning',
'xiaotian',
'knight'
],
[Symbol.iterator](){
//索引变量
let index = 0;
let _this = this;
return {
next:function(){
if (index < _this.stus.length) {
const result = {
value:_this.stus[index],
done:false
};
//下标自增
index++;
return result;
}else {
return {
value:undefined;
done:true
}
}
}
};
}
}
//遍历这个对象,使用for...of遍历,返回的是这个属性数组里的每一个成员
for (let v of banji) {
console.log(v);
}
十二、生成器
生成器是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同
生成器是一种特殊的函数,来进行异步编程
function * gen(){
//yield作为函数代码的分隔符,三个分隔符产生四个代码
yield '一只没有耳朵';
yield '一只没有尾巴';
yield '真奇怪';
}
for (let v of gen){
console.log(v);
}
function * gen (arg) {
console.log(arg); //AAA
let one = yield 111;
console.log(one); //BBB
let two = yield 222;
console.log(two); //CCC
let three = yield 333;
console.log(three); //DDD
}
//执行获取迭代器对象
let iterator = gen('AAA');
console.log(itrator.next()); //{value: 111, done: false}
//next方法可以传入实参
console.log(itrator.next('BBB')); //{value: 222, done: false}
console.log(itrator.next('CCC')); //{value: 333, done: false}
console.log(itrator.next('DDD')); //{value: undefined, done: true}
生成器实例
1、回调地狱
//1s 后控制台输出 111 2s后输出222 3s后输出333
setTimeout(() => {
console.log(111);
setTimeout(() => {
console.log(222);
setTimeout(() => {
console.log(333);
},3000);
},2000);
},1000);
function one() {
setTimeout(() => {
console.log(111);
iterator.next();
},1000)
}
function two() {
setTimeout(() => {
console.log(222);
iterator.next();
},2000)
}
function three() {
setTimeout(() => {
console.log(333);
iterator.next();
},3000)
}
function * gen() {
yield one();
yield two();
yield three();
}
//调用生成器函数
let iterator = gen();
iterator.next();
2、 模拟获取
//模拟获取 用户数据 订单数据 商品数据
//先后顺序,依次获取
function getUsers() {
setTimeout(() => {
let data = '用户数据';
//调用next方法,并且将数据传入
iterator.next(data);
}, 1000);
}
function getOrders() {
setTimeout(() => {
let data = '订单数据';
iterator.next(data);
}, 1000);
}
function getGoods() {
setTimeout(() => {
let data = '商品数据';
iterator.next(data);
}, 1000);
}
function* gen() {
let users = yield getUsers();
console.log(users);
let orders = yield getOrders();
console.log(orders);
let goods = yield getGoods();
console.log(goods);
}
//调用生成器函数
let iterator = gen();
iterator.next();
十三、Promise(面试常问)
1、基础
1. 1区别实例对象与函数对象
① 实例对象:new函数产生的对象,称为实例对象,简称为对象
② 函数对象:将函数作为对象使用时,称为函数对象
//括号的左边的函数, 点的左边是对象
function Fn() { //Fn是函数
const fn = new Fn() //此时Fn是构造函数 fn是实例对象,简称为对象
console.log(Fn.prototype) //Fn是函数对象
Fn.blind({}) //Fn是函数对象
$('#text') //jQuery函数
$.get('/test') //jQuery函数对象
}
1. 2 两种类型的回调函数
1.2.1 同步回调
概念:立即执行,完全执行完了才结束,不会放入回调队列中
例子:数组遍历相关的回调函数/Promise的excutor函数
const arr = [1, 3, 5]
arr.forEach(item => {
console.log(item); //遍历的回调,同步回调函数,不会放入队列,一上来就要执行完
})
console.log('forEach()之后');
1.2.2 异步回调
概念:不会立即执行,会放入回调队列中将来执行
例子:定时器回调、Ajax回调、Promise的成功|失败的回调
setTimeout(() => {
console.log('timeout callback()'); //异步回调函数,会放入队列中将来执行
}, 0);
console.log('setTimeout()之后');
1.3 JS的error处理
1.3.1 错误的类型
① Error:所有错误的父类型
② ReferenceError:引用的变量不存在
③ TypeError:数据类型不正确的错误
④ RangError:数据值不在其所允许的范围内
⑤ SyntaxError:语法错误
1.3.2 错误处理
① 捕获错误:try...catch
try {
let b
console.log(b.xxx);
} catch (error) {
console.log(error);
//TypeError: Cannot read properties of undefined (reading 'xxx')
}
② 抛出错误:throw error
function something() {
if (Date.now() % 2 === 1) {
console.log('当前的时间为奇数,可以执行任务');
} else { //抛出异常,由调用者来处理
throw new Error('当前时间为偶数,无法执行任务')
}
}
//捕获处理异常
try {
something()
} catch (error) {
alert(error.message)
}
2、 promise的理解和使用
2.1 Promise是什么
① 抽象表达:Promise是JS中进行异步编程的新的解决方案(旧的是纯回调形式)
② 具体表达:
(1)从语法上来说:Promise是一个构造函数
(2)从功能上来说:Promise对象用来封装一个异步操作并可以获取其结果
2.2 Promise的状态改变
① pendding变为resolved
② pedding变为rejected
说明:只有这2种,且一个promise对象只能改变一次
无论变为成功还是失败,都会有一个结果数据
成功的结果数据一般称为value,失败的结果数据一般称为reason
2.3 Promise的基本流程
2.4 Promise的基本使用
//1、创建一个新的promise对象
const p = new Promise((resolve, reject) => { //执行器函数
//2、执行异步操作任务
setTimeout(() => {
const time = Date.now() //如果当前时间是偶数,就代表成功,否则代表失败
// 3、(1)如果成功了,调用resolve(value)
if (time % 2 == 0) {
resolve('成功的数据, time =' + time)
} else {
// (2)如果失败了,调用rejecte(reason)
reject('失败的数据,time' + time)
}
}, 1000);
})
//4、
p.then(
value => { //接收得到成功的value数据 onResolved
console.log('成功的回调', value);
},
reason => { //接收得到失败的reason数据 onRejected
console.log('失败的回调', reason);
}
)
2.5 为什么要用Promise ?
① 使指定回调函数的方式更加灵活
(1)旧的:必须在启动异步任务前指定
(2)promise:启动异步任务 => 返回promise对象 => 给promise对象绑定回调函数 (甚至可以在异步任务启动后就指定)
// 1.1使用纯回调函数
createAudioFileAsync(audioSettings, successCallback, failureCallback)
// 1.2使用promise
const promise = createAudioFileAsync(audioSettings);
setTimeout(() => {
promise.then(successCallback, failureCallback);
}, 3000);
② 支持链式调用,可以解决回调地狱的问题
(1)什么是回调地狱?回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调函数执行的条件(内层的结果为外层的参数)
(2)回调函数的缺点?不便于阅读/不便于异常处理
(3) 解决方案?promise链式调用
(4)终极解决方案?async/await
//2.1回调地狱
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result' + finalResult);
}, failureCallback)
}, failureCallback)
}, failureCallback)
//2.2 使用promise的链式调用解决回调地狱
doSomething().then(function(result) {
return doSomethingElse(result)
})
.then(function(newResult) {
return doThirdThing(newResult)
})
.then(function(finalResult) {
lconsole.log('Got the final result' + finalResult)
})
.catch(failureCallback) //异常传透
//2.3 async/await :回调地狱的终极解决方案
asnyc
function request() {
try {
const result = await doSomething();
const newResult = await doSomethingElse(result);
const finalResult = await doThirdThing(newResult);
console.log('Got the final result' + finalResult)
} catch (error) {
failureCallback(error)
}
}
2.6 如何使用Promise ?
2.6.1 API
一、Promise构造函数:Promise(excutor){ }
(1)excutor函数:执行器(resolve,reject)=> { }
(2)resolve函数:内部定义成功时我们调用的函数value =>{ }
(3)reject函数:内部定义失败时我们调用的函数 reason =>{ }
说明:excutor会在Promise内部立即同步回调,一步操作在执行器中执行
二、Promise.prototype.then方法:(onResolved,onRejected)=>{ }
onResolved函数:成功回调函数 (value)=> { }
onRejected函数:失败回调函数 (reason)=> { }
说明:指定用于得到成功value的成功回调和用于得到失败reason的失败回调
返回一个新的promise对象
三、Promise.prototype.catch方法:(onRejected)=> { }
onRejected函数:失败的回调函数 (reason)=> { }
说明:then()的语法糖,相当于:then(undefined,onRejected)
四、Promise.resolve方法:(value)=> { }
value:成功的数据或promise对象
说明:返回一个成功/失败的promise对象
五、Promise.reject方法:(reason)=> { }
reason:失败的原因
说明:返回一个失败的promise对象
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功的数据')
//只能执行一次reject('失败的数据')
}, 1000)
}).then(
value => {
console.log('onResolved()1', value);
}
).catch(
reason => {
console.log('onRejected()', reason);
}
)
const p1 = new Promise((resolve, reject) => {
resolve(1)
})
const p2 = Promise.resolve(2);
const p3 = Promise.reject(3);
p1.then(value => {
console.log(value)
})
p2.then(value => {
console.log(value)
})
p3.catch(reason => {
console.log(reason)
})
2.6.2 Promise的几个关键问题
一、如何改变Promise的状态?
(1)resolve(value):如果当前是pendding就会变为resolved
(2)reject(reason):如果当前是pendding就会变为rejectd
(3)抛出异常:如果当前是pendding就会变为rejectd
const p = new Promise((resolve, reject) => {
// resolve(1) //promise变为resolved状态
// reject(2) //promise变为rejected状态
// throw new Error('出错了') //抛出异常,promise变为rejected失败状态,reason抛出的error
throw 3
})
p.then(
value => {},
reason => {
console.log('reason', reason); // 'reason', 3
}
)
二、一个Promise指定多个成功/失败回调函数,都会调用嘛?
当Promise改变为对应状态时都会调用
三、改变Promise状态和指定回调函数谁先谁后?
(1)都有可能,正常情况下是先指定回调再改变状态,但也可以先改变状态再指定回调
(2)如何先改状态再指定回调?
① 在执行器中直接调用resolve()/ reject()
② 延迟更长时间才能调用then()
(3)什么时候才能得到数据?
① 如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据
② 如果先改变的状态,那当指定回调时,回调函数就会调用,得到数据
//1、常规:先执行回调函数,后改变状态
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1) //后改变状态(同时指定数据),异步执行回调函数
}, 1000);
}).then( //先指定回调函数,保存当前指定的回调函数
value => {},
reason => {
console.log('reason', reason);
}
)
//2、先改状态,后执行回调函数
new Promise((resolve, reject) => {
resolve(1) //先改变状态(同时指定数据)
}).then( //后指定回调函数,异步执行回调函数
value => {
console.log('value', value);
},
reason => {
console.log('reason', reason);
}
)
四、Promise.then()返回的新的Promise的结果状态由什么决定?
(1)简单表述:由then()指定的回调函数执行的结果决定
(2)详细表述:
① 如果抛出异常,新Promise变为rejected,reason为抛出的异常
② 如果返回的是非Promise的任意值,新Promise变为resolved,value为返回的值
③ 如果返回的是另一个新Promise,此Promise的结果就会成为新Promise的结果
五、Promise如何串连多个操作任务
(1)Promise的then()返回一个新的Promise,可以开成then()的链式调用
(2)通过then的链式调用串连多个同步/异步任务
六、Promise异常传透
(1)当使用Promise的then链式调用时,可以在最后指定失败的回调
(2) 前面任何操作出了异常,都会传到最后失败的回调中处理
七、中断Promise链
(1)当使用Promise的then链式调用时,在中间中断,不再调用后面的回调函数
(2)办法:在回调函数中返回一个pendding状态的Promise对象
3、自定义(手写)Promise
3.1 定义整体结构(创建promise.js)
/*
自定义Promise函数模块:IFFE
*/
(function(window) {
/*
Promise构造函数
executor:执行器函数(同步执行)
*/
function Promise(excutor) {
//resolve函数
function resolve(data) {
}
//reject函数
function reject (data) {
}
//同步调用执行器函数
executor(resolve,reject);
}
/*
Promise原型对象的then()
1.指定成功和失败的回调函数
2.函数返回一个新的promise对象
*/
Promise.prototype.then = function(onResolve, onReject) {
}
/*
Promise原型对象的catch()
1.指定失败的回调函数
2.函数返回一个新的promise对象
*/
Promise.prototype.catch = function(onRejectd) {
}
/*
Promise函数对象的resolve方法
返回一个指定结果的成功promise
*/
Promise.resolve = function(value) {
}
/*
Promise函数对象的reject方法
返回一个指定结果的失败promise
*/
Promise.reject = function(reason) {
}
/*
Promise函数对象的all方法
返回一个promise,只有当所有promise都成功时,才成功,否则有一个失败,都失败
*/
Promise.all = function(promises) {
}
/*
Promise函数对象的race方法
返回一个promise,其结果由第一个完成的promise来决定
*/
Promise.race = function(promises) {
}
//向外暴露promise函数
window.Promise = Promise
})(window)
3.2 Promise构造函数的实现
3.3 Promise.then()/ catch()的实现
3.4 Promise.resolve() / reject()的实现
//添加resolve方法
Promise.resolve = function(value) {
//返回promise对象
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(v => {
resolve(v);
}, r => {
reject(r);
})
} else
//状态设置成功、
resolve(value)
});
}
//添加reject方法
Promise.reject = function(reason) {
//返回promise对象
return new Promise((resolve, reject) => {
reject(reason);
});
}
3.5 Promise.all()的实现
Promise.all = function(promises) {
//返回结果为promise对象
return new Promise((resolve, reject) => {
//遍历
for (let i = 0; i < promises.length; i++) {
//声明变量
let count = 0;
let arr = [];
//
promises[i].then(v => {
//得知对象的状态是成功的
//每个promise对象都成功,才能执行resolve函数
count++;
//将当前promise对象成功的结果存入数组
arr[i] = v;
//判断
if (count === promises.length) {
resolve(arr);
}
}, r => {
reject(r);
})
}
});
}
3.5 Promise.all / race()的实现
Promise.race = function(promises) {
return new Promise((resolve,reject)=>{
for(let i = 0 ; i < promises.length;i++){
promises[i].then(v=>{
//修改返回对象值为成功
resolve(v);
},r=>{
//次该范湖对象为失败
reject(r);
})
}
});
}
4、async与await
4.1 async函数
(1)函数的返回值为promise对象
(2)promise对象的结果由async函数执行的返回值决定
//和then返回规则一样
async function main() {
//1、如果返回值是一个非promise类型的数据
// return 521;
//2、如果返回值是一个promise对象
return new Promise((resolve, reject) => {
// resolve('ok');
reject('error');
})
//3、抛出异常
throw "oh no";
};
let result = main();
console.log(result);
4.2 await表达式
(1)await右侧的表达式一般为promise对象,但也可以是其他的值
(2)如果表达式是promise对象,await返回的是promise成功的值
(3)如果表达式是其他值,直接将此值作为await的值
注意:
(1)await必须写在async函数中,但async函数中可以没有await
(2)如果await的promise失败了,就会抛出异常,需要通过try...catch捕获处理
//await必须写在async函数中,但async函数中可以没有await
// await 10; //Uncaught SyntaxError: await is only valid in async functions and the top level bodies of modules
async function main() {
let p = new Promise((resolve, reject) => {
// resolve('ok');
reject('error');
});
//1、右侧为promise的情况
// await返回值是promise成功的值
let res = await p;
console.log(res); //ok
//2、右侧为其他类型的数据
//直接将此值作为await的值
let res2 = await 20;
console.log(res2); //20
//3、如果promise是失败的状态
try {
let res3 = await p;
} catch (e) {
console.log(e); //error
}
}
main();
十四、Set
ES6提供了新的数据结构Set(集合)。它类似于数组,但成员的值都是唯一的,集合实现了iterator接口,所以可以使用扩展运算符和for...of进行遍历,集合的属性和方法:
(1)size 返回集合的元素个数
(2)add 增加一个新元素,返回当前集合
(3)delete 删除元素,返回boolean值
(4)has 检测集合中是否包含某个元素,返回boolean值。
//声明一个set
let s = new Set();
let s2 = new Set(['大事', '小事', '没事']);
console.log(s, typeof s);
//Set(0) {size: 0}[[Entries]]无属性size: 0[[Prototype]]: Set 'object'
console.log(s2, typeof s2);
//Set(3) {'大事', '小事', '没事'} 'object'
//1、元素个数
console.log(s2.size); //3
//添加新的元素
s2.add('喜事');
console.log(s2); //Set(4) {'大事', '小事', '没事', '喜事'}
//删除元素
s2.delete('喜事');
console.log(s2);
//2、检测
console.log(s2.has('大事')); //true
//3、清空
s2.clear();
console.log(s2); //Set(0) {size: 0}
//遍历
for (let v of s2) {
console.log(v);
}
案例:
let arr = [1, 2, 3, 4, 5, 4, 3, 2, 1];
//1、数组去重
let result = [...new Set(arr)];
console.log(result);
//2、交集
let arr2 = [4, 5, 6, 5, 6];
//filter创建一个数组,符合条件的都放进去(过滤器)
let result = [...new Set(arr)].filter(item => {
let s2 = new Set(arr2); //4.5.6
if(s2.has(item)){
return true;
}else {
return false;
}
});
//简写
let result = [...new Set(arr)].filter(item => new Set(arr2).has(item));
console.log(result); //[4,5]
//3、并集
let union = [...new Set([...arr, ...arr2])];
console.log(union); //合并arr和arr2数组 [1, 2, 3, 4, 5, 6]
//4、差集
//用arr和arr2比较
let diff = [...new Set(arr)].filter(item => !(new Set(arr2).has(item)));
十五、Map
ES6提供了Map数据结构。它类似于对象,也是键值对的集合。但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当做键。Map也实现了iterator接口,所以可以使用扩展运算符和for...of进行遍历。Map的属性和方法:
(1)size 返回Map的元素个数
(2)set 增加一个新元素,返回当前Map
(3)get 返回键名对象的键值
(4)has 检测Map中是否包含某个元素,返回Boolean值
(5)clear 清空集合,返回undefined
十六、class类
function Phone() {
}
// Phone是函数对象,属于类,不属于实例对象
Phone.name = '手机';
Phone.change = function() {
console.log('我可以改变世界');
}
Phone.prototype.size = '5.5inch';
//nokia是实例对象
let nokia = new Phone();
console.log(nokia.name); //undefined
console.log(nokia.size); //5.5inch
class Phone {
//静态属性 static标注的属性和方法,属于class类,不属于实例对象vivo
static name = '手机';
static change() {
console.log('手机真好玩');
}
}
let vivo = new Phone();
console.log(vivo.name); //undefined
console.log(Phone.name); //手机
十七、数值扩展
1、Number.EPSILON是JavaScript表示的最小精度
EPSILON属性的值接近于2.2204460492503130808472633361816E-16(主要用作在浮点数上)
function equal(a, b) {
if (Math.abs(a - b) < Number.EPSILON) {
return true;
} else {
return false;
}
}
console.log(0.1 + 0.2 === 0.3); //false
console.log(equal(0.1 + 0.2, 0.3)); //true
2、二进制和八进制
let b = 0b1010; //二进制
let o = 0o777; //八进制
let d = 100; //十进制
let x = 0xff; //十六进制
console.log(b); //10
console.log(d); //100
3、Number.inFinite 检测一个数值是否为有限数
console.log(Number.isFinite(100)); //true
console.log(Number.isFinite(100 / 0)); //false
console.log(Number.isFinite(Infinity));//false
4、Number.isNaN 检测一个数值是否为NaN
console.log(Number.isNaN(123)); //false
5、Number.parseInt / Number.parseFloat字符串转整数
console.log(Number.parseInt('5201314love')); //5201314
console.log(Number.parseFloat('3.1415926神奇'));//3.1415926
6、Number.isInteger 判断一个数是否为整数
console.log(Number.isInteger(5)); //true
console.log(Number.isInteger(2.55));//false
7、Math.trunc 将数字的小数部分抹掉
console.log(Math.trunc(3.5)); //3
8、Math.sign 判断一个数到底为正数、负数还是零
console.log(Math.sign(100)); //1
console.log(Math.sign(0)); //0
console.log(Math.sign(-250)); //-1
十八、对象扩展
1、Object.is 判断两个值是否完全相等
console.log(Object.is(120, 121)); //作用和===很像
console.log(Object.is(NaN, NaN)); //true
console.log(NaN === NaN); //false
2、Object.assign对象的合并
const config1 = {
hose: 'localhost',
port: 3306,
name: 'root',
pass: 'root',
text: 'test',
};
const config2 = {
host: 'http://baidu.com',
port: 33060,
name: 'baidu',
pass: 'iloveyou'
};
console.log(Object.assign(config1, config2));
//{hose: 'localhost', port: 33060, name: 'baidu', pass: 'iloveyou', text: 'test', …}
// hose: "localhost"
// host: "http://baidu.com"
// name: "baidu"
// pass: "iloveyou"
// port: 33060
// text: "test"
// [[Prototype]]: Object
3、Object.setPrototypeOf / Object.getPrototypeOf
const school = {
name: 'beida'
};
const city = {
xiaoqu: ['北京', '上海', '深圳']
};
Object.setPrototypeOf(school, city);
console.log(Object.getPrototypeOf(school)); //{xiaoqu: Array(3)}
console.log(school); //{name: 'beida'}
十九、模块化
模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。
1、模块化的好处
(1)防止命名冲突
(2)代码复用
(3)高维护性
2、模块化规模产品
ES6之前的模块化规范有:
(1)CommonJs => NodeJs、Browserify
(2)AMD => requireJS
(3)CMD => seaJS
3、ES6模块化语法
模块功能主要由两个命令构成:export和import
(1)export命令用于规定模块的对外接口
(2)import命令用于输入其他模块提供的功能