ES6
一 学习ES6前的准备
1 区别实例对象与函数对象
function Fn() { // Fn是函数
}
var fn = new Fn() // Fn是构造函数 new返回的是实例对象
console.log(Fn.prototype) // Fn是函数对象
Fn.call({}) // Fn是函数对象
$('#test') $.each // $是函数
$.ajax() // $是函数对象
/*
总结
1. 点的左边是对象(可能是实例对象也可能是函数对象)
通过new操作符实例化出来的对象称为实例对象。 var obj = {};// new Object
2. ()的左边是函数,通过function定义的 运行时通过()
*/
2 二种类型的回调函数
2.1 同步回调
-
理解: 立即执行, 完全执行完了才结束, 不会放入回调队列中
-
例子: 数组遍历相关的回调函数
Promise
的excutor
函数const arr = [1, 2, 3] arr.forEach(item => {// 同步执行的回调函数, forEach()内部在执行完所有回调函数后才结束, 不会放入回调队列中 console.log('forEach callback()', item) }) console.log('forEach()之后') 自上而下执行 先执行遍历的,再执行后面的输出
2.2 异步回调
-
理解: 不会立即执行, 会放入回调队列中将来执行
-
例子: 定时器回调 ajax 回调 Promise的成功|失败的回调
// 2. 异步回调函数 setTimeout(() => { // 异步执行的回调函数, setTimeout()在回调函数执行前就结束了, 回调会放入回调队列中在同步代码执行完后才会执行 console.log('setTimeout callback()') }, 0) console.log('setTimeout()之后') 先输出全局,再输出setTimeout
2.3 异步函数的使用
function ajax(cb) {
var xhr = new XMLHttpRequest();
xhr.open("get","1、区别实例对象与函数对象.html");
xhr.send();
xhr.onload = function() {
cb(xhr.responseText)
}
}
// 假设出现两种需求,则需要使用异步回调函数,进行传参,再调用
// A:输出
ajax(function(res) {
console.log(res);
});
// B:弹出
ajax(function(res) {
alert(res)
})
3 JS
的error
处理
3.1 错误的类型
1、 ReferenceError
(引用错误):使用了未定义的变量。错误之前的代码会执行,之后代码不会执行。
// 1、变量未定义便直接使用
console.log(my);
// 报错:Uncaught ReferenceError: my is not defined
// 翻译:my未定义
// 2、将变量赋值给一个无法被赋值的小编
Math.random()=1;
// 报错:Uncaught ReferenceError: Invalid left-hand side in assignment
// 翻译:左侧的赋值无效
2、TypeError
(类型错误):变量或参数不是预期类型,或调用对象不存在的属性方法。错误之前的代码会执行,之后代码不会执行。
// 1、变量不是预期类型,比如对字符串、布尔值、数值等原始类型的值使用new命令。
let userName = new "zhangpeiyue";
// 报错:Uncaught TypeError: "zhangpeiyue" is not a constructor
// 翻译:"zhangpeiyue" 不是一个构造函数。new 操作符后应该是一个构造函数
// 2、变量不是预期类型,比如变量被作为函数来使用
let userName = "zhangpeiyue";
console.log(userName())
// 报错:Uncaught TypeError: userName is not a function
// 翻译:userName 不是一个函数
// 3、对象的属性或方法不存在
const obj = undefined;// 为null也会报错
console.log(obj.userName);
// 报错:Uncaught TypeError: Cannot read property 'userName' of undefined
// 翻译:undefined的环境下无法读取属性“userName”
// var obj = { }
// console.log(obj.a.b) obj.a 为undefined obj.a.b报错
// var div = document.querySelector('div');
// console.log(div); null
// div.background ='' 不存在的属性不能修改
// var obj = new '';
// var obj = '';
// obj(); //为空,没有被定义
3、RangeError
(范围错误): 数据值不在JS
所允许的范围内。错误之前的代码会执行,之后代码不会执行。
// 1、递归函数未设置跳出的条件
function run(){
run();
}
run();
// 报错:Uncaught RangeError: Maximum call stack size exceeded
// 翻译:超出最大调用堆栈大小。原因函数一直调用,直到达到调用堆栈限制。
// 2、无效的数组长度,应该是个正整数
const arr =new Array(-1);
// 报错:Uncaught RangeError: Invalid array length
// 翻译:无效的数组长度
4、SyntaxError
(语法错误):即写的代码不符合js
编码规则。我们可以根据后面的信息提示去修改错误,当然,语法错误,浏览器会直接报错,整个代码都不会执行。
// 1、程序错误,比如写错,或者缺少 , ) ; } 这些符号。
const obj = {;
// 报错:Uncaught SyntaxError: Unexpected token ';'
// 翻译:";"该标记有些出乎意料。
// 2、变量定义不合法
let 8userName = "zhangpeiyue";
// 报错:Uncaught SyntaxError: Invalid or unexpected token
// 翻译:定义的变量标记无效
// 3、对象属性赋值语法错误
const obj = {
userName = "zhangpeiyue"
}
// 报错:Uncaught SyntaxError: Invalid shorthand property initializer
// 翻译:对象属性初始值无效。原因:对象中属性与其对应的值之间使用“=”
3.2 错误处理
1、被try
包裹的代码块一旦出现Error
,会将Error
传递给catch
并运行catch
代码块。不会影响后续代码运行。
try{
console.log(userName); //可能会出现错误的代码
}catch (err) {
// ReferenceError: userName is not defined
console.log(err); //当try出现非语法错误时,会执行catch
} finally {
console.log("我还会继续运行哦!!") //不管try当中是否有异常,都会执行
}
try{
// Uncaught SyntaxError: Invalid or unexpected token
const 8userName = "zhangpeiyue";
}catch (err) {
console.log(err);
}
console.log("我不会继续运行了!!")
// 异步回调 下面捕获不到错误信息 直接报错,没有捕获到
try {
setTimeout(function() {
var arr = new Array(-1);
})
} catch(e) {
console.log(e);
}
// 将try放置到内部可以捕获的到
setTimeout(function () {
try{
var arr = new Array(-1);
}catch (e) {
console.log(e);
}
})
3、通过 throw new Error 抛出错误
try{
throw new Error("出现异常了");
}catch (err) {
// 错误相关信息
console.log(err.message);// 出现异常了
// 函数调用栈记录信息
console.log(err.stack);// Error: 出现异常了
}
console.log("我还会继续运行哦!!")
4、不管有没有异常,finally中的代码都会在try和catch之后执行
try{
throw new Error("出现异常了");
}catch (err) {
// 错误相关信息
console.log(err.message);// 出现异常了
// 函数调用栈记录信息
console.log(err.stack);// Error: 出现异常了
}finally {
// 不管有没有异常,我都会执行。哪怕你有return,我也会执行!
console.log("不管有没有异常,我都会执行。哪怕你有return,我也会执行!")
}
console.log("我还会继续运行哦!!")
3.3 error对象的结构
3.4 总结
try{
//可能发生错误的代码
}catch(err){
//只有发生错误时才执行的代码
}finally{
//无论是否出错,肯定都要执行的代码
}
对象与字符串之间的转换
var obj = {a:1,b:2};
console.log(typeof obj);// object
var str = JSON.stringify(obj);// 将obj转为字符串,并将转换后的字符串赋值给str;
console.log(typeof str);
var obj2 = JSON.parse(str);// 将str转为对象,并将转换后的对象赋值给obj2;
console.log(obj2);//object
4 网址(URL)的构成
1.网址(URL):即统一资源定位符,其具体使用主要体现在用户通过浏览器访问服务器(B/S模式)
2.构成:
// http://www.zhangpeiyue.com:8090/home/index?a=1&b=2#one
// http://127.0.0.1:8090/home/index?a=1&b=2#one
协议://服务 域名( IP:端口)/URI?key1=value1&key2=value2#xxxx
- 协议:http、https、ftp…
- 服务:万维网(World Wide Web )
- 域名 = 标识串(baidu 、google、sina…)+网站类型(com、gov、edu…)
- IP地址:127.0.0.1
- 端口号:以数字形式表示,若协议为HTTP,默认端口号为80,若协议为HTTPS,默认端口号为443,默认端口号可省略不写,同一服务器端口号不允许重复。 端口的取值范围是:0-65535
- URI:统一资源标识符(Web上可用资源的具体地址)
- 查询:以“?”为起点,每个参数以“&”隔开,以“=”分开参数名称与数据
- 片段:以“#”字符开头
5 严格模式
5.1、介绍
ES5 除了正常运行模式(又称为混杂模式),还添加了第二种运行模式:“严格模式”(strict mode)。
严格模式顾名思义,就是使 JavaScript 在更严格的语法条件下运行。
5.2、作用
5.3、使用
-
在全局或函数的第一条语句定义为:
'use strict'
-
如果浏览器不支持,只解析为一条简单的语句, 没有任何副作用
// 全局使用严格模式 'use strict'; girl = '迪丽热巴'; // 函数中使用严格模式 function main(){ 'use strict'; boy = '吴亦凡'; } main();
5.4、语法和行为改变
-
必须用 var 声明变量,不允许使用未声明的变量
"use strict" function fn() { a = 1; } fn() //不会报错 在严格模式下,会报引用错误
"use strict"
var a = 1;
function fn() {
// console.log(this.a) //1
// console.log(window.a) //1
// console.log(this === window ) //ture
console.log(this) //在严格模式下,this指向的是undefined
}
fn()
- 创建 eval 作用域
// 创建 eval 作用域.在严格模式下会拥有自身的作用域。
"use strict"
// 将字符串作为JS来执行。
var str = "console.log(123);"; //123
eval(str);
//严格模式下会报错:ReferenceError: a is not defined
eval("var a = 1;console.log(a);") //1
console.log(a); //1
var a = 1;
eval("a+=1;console.log(a);") //2
console.log(a); //2
-
对象不能有重名的属性(Chrome 已经修复了这个 Bug,IE 还会出现)
"use strict" var obj = { a:1, a:2 } console.log(obj); //{a:2}
-
函数不能有重复的形参
"use strict" function fn(a,a){// SyntaxError: Duplicate parameter name not allowed in this context console.log(a);// 2 } fn(1,2);
-
新增一些保留字, 如: implements interface private protected public
// SyntaxError: Unexpected strict mode reserved word
"use strict"
var public = "abc";
console.log(public);
function fn(){
"use strict"
// a = 1;
// console.log(a);
}
fn(); SyntaxError错误
6 Object 扩展方法
6.1、Object.create(prototype, [descriptors])
Object.create 方法可以以指定对象为原型创建新的对象,同时可以为新的对象设置属性, 并对属性进行描述
- value : 指定值
- writable : 标识当前属性值是否是可修改的, 默认为 false
- configurable:标识当前属性是否可以被删除 默认为 false
- enumerable:标识当前属性是否能用for in 枚举 默认为 false
- get set :读写器,不和以上属性结合使用,根据情况可以只用其中一个。
- get: 当获取当前属性时的回调函数 该函数的的返回值,就是该属性的值
- set: 当设置当前属性时
//创建一个汽车的对象
var car = {
name : '汽车',
run: function(){
console.log('我可以行驶!!');
}
};
//以 car 为原型对象创建新对象
var aodi = Object.create(car, {
brand: {
value: '奥迪',
writable: false, //是否可修改
configurable: false, //是否可以删除
enumerable: true //是否可以使用 for...in 遍历
},
color: {
value : '黑色',
wriable: false,
configurable: false,
enumerable: true
}
});
下面即可进行修改,删除 遍历
aodi.color = red
var obj2 = Object.create(obj,{
bookName:{
get:function () {
return "《"+this._bookName+"》"
},
set:function (bookName) {
this._bookName = bookName;
}
}
})
obj2.bookName = "天龙八部";
console.log(obj2.bookName); //《天龙八部》
6.2、Object.defineProperties(object, descriptors)
- object 要操作的对象
- descriptors 属性描述
// 定义对象
var star = {
firstName: '刘',
lastName : '德华'
};
// 为 star 定义额外的属性
Object.defineProperties(star, {
fullName: {
get: function(){
return this.firstName + this.lastName;
},
set: function(name){
var res = name.split('-');
this.firstName = res[0];
this.lastName = res[1];
}
}
});
// 修改 fullName 属性值
star.fullName = '张-学友';
// 打印属性
console.log(star.fullName); //张学友
案例:统计数据
Object.defineProperties(obj,{
total:{
get:function() {
var num = 0;
this.chengji.forEach(function(item) {
num += item.score;
})
return num;
}
},
avg:{
get:function() {
return this.total/this.chengji.length
}
}
})
console.log(obj.total,obj.avg) //112 28
6.3、call、apply 和 bind
function main(){
console.log(this);
}
/*1. 直接调用函数*/
main(); // window
/*2. 创建一个对象*/
var company = {name: '尚硅谷', age: 10};
/*使用这个对象调用 main 方法*/
main.call(company); // company
main.apply(company); // company
/*bind 修改 this 的值,返回一个新的函数*/
var fn = main.bind(company);
fn(); // company
7 块级作用域
一对花括号 {}
中的语句属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域。
// 条件语句
if () {}
// switch语句
switch () {}
// for / while循环语句
for () {}
while () {}
// try...catch语句
try () catch (err) {}
// 单大括号
{}
// function fn() {
// var a = 1;
// }
// fn();
// console.log(a); ReferenceError
var a = 1;
if(a === 1){
var b = 2;
}
console.log(b); //2
{
var b = 3;
}
console.log(b); //3
1 let 和 const 关键字
1.1 let 关键字
① 作用
声明变量,用来代替 var 。 let 声明的变量具备变量的基本特性。
let 变量名 = 值;
② 特点
let 声明的变量具有如下特点:
- let 声明的变量不能重复声明。
let userName = "zhangliu";
let userName = "yanqi"
2、块级作用域
if(true){
let c = 3;
}
console.log(c); //ReferenceError
{
let a = 1;
}
console.log(a); //ReferenceError
3、不存在变量提升
console.log(a);// undefined
var a = 1;
console.log(a);
let a = 1; // ReferenceError: Cannot access 'a' before initialization
4、不影响作用域链:会层级往上找
function fn() {
let userName = "zhangsan";
function _child(){
// let userName = "child";
console.log(userName);
}
_child();
}
fn(); //zhangsan
var userName = "lisi";
console.log(window.userName);// lisi
console.log(this.userName);// lisi
console.log(this === window);// true
let userName = "lisi";
console.log(window.userName);// undefined
console.log(this.userName);// undefined
console.log(this === window);// true
1.2 块级作用域
产生块级作用域的语法:
// 1. {} 语法
{
语句;
}
// 2. for / while 循环结构
for () {
}
while () {
}
// 3. if / else / switch 分支结构
if () {
} else {
}
switch () {
}
// 4 try catch
try {
} catch ();
1.3 const 关键字
① 作用
为了与变量区分,一般常量名采用大写(潜规则)。
const 常量 = 值;
② 特点:
- const 声明的常量,不能重新声明也不能重新赋值。
- 全局常量不会作为顶层全局对象的属性。
- 不能提升
- 常量在全局作用域和局部作用域的基础上新增了块级作用域。
案例:切换
// 设置切换,第一种方法:forEach
var btns = document.querySelectorAll('button')
var index = 0
btns[index].style.background = 'red';
btns.forEach(function(item,i) {
item.onclick = function() {
btns[index].style.background = null;
index = i;
btns[index].style.background = 'red'
}
})
// 第二种 for(考虑作用域):先点击,再自调用函数,传参数i.再返回一个函数
var btns = document.querySelectorAll('button')
var index = 0
btns[index].style.background = 'red';
for(var i = 0; i < btns.length; i ++) {
btns[i].onclick = (function(i) {
return function() {
btns[index].style.background = null;
index = i;
btns[index].style.background = 'red'
}
})(i)
}
// 第三种 for:先自调用,再进行点击事件,传参数i.不用返回,直接设置
var btns = document.querySelectorAll('button')
var index = 0
btns[index].style.background = 'red';
for(var i = 0;i < btns.length; i ++) {
(function(i) {
btns[i].onclick = function() {
btns[index].style.background = null;
index = i;
this.style.background = "red"
}
})(i)
}
// 第四种 for:使用自调用函数,再使用bind改变this的指向
var btns = document.querySelectorAll('button')
var index = 0
btns[index].style.background = 'red';
for(var i = 0; i < btns.length; i ++) {
btns[i].onclick = (function(i) {
btns[index].style.background = null;
index = i;
btns[index].style.background = 'red'
// this.style.background = "red";
}.bind(btns[index],i))
}
// 第五种 for:使用data自定义属性
var btns = document.querySelectorAll('button')
var index = 0
btns[index].style.background = 'red';
for(var i = 0; i < btns.length; i ++) {
btns[i].dataset.index = i;//自定义属性
btns[i].onclick = function() {
console.log(this.dataset.index) //索引
btns[index].style.background = null;
index = this.dataset.index;
this.style.background = 'red'
}
}
// 第六种 for:使用let
var btns = document.querySelectorAll('button')
var index = 0
btns[index].style.background = 'red';
for (let i = 0;i <btns.length; i ++) {
btns[i].onclick = function() {
console.log(i) //索引
btns[index].style.background = null;
index = i;
btns[index].style.background = 'red'
}
}
2 箭头函数
2.1 箭头函数的基本写法
① function省略
② 在()与{}之间添加 =>
const fn = t => t;
//相当于
const fn = function(t) {
return t;
}
//含有参数的
const fn = function (c,d) {
var a = 1;
var b = 2;
return a+b+c+d;
}
// 转换为箭头函数
const fn = (c, d) => {
var a = 1;
var b = 2;
return a + b + c + d;
}
console.log(fn(3, 4));
③ 如果只有一个参数,()可以省略,多个或0个不可以省略
// 无参数,()不可以省略
const fn = () => {
return 3;
}
// 多个参数,()不可以省略
const fn = (a,b) => {
return a+b;
}
// 一个参数,()可以省略
const fn = a => {
return a ;
}
④ 如果函数体内只有一行代码,那么{}可以省略,return也可以省略;函数体内有多行,那么{}是不可以省略的。
const fn = a => a //相当于 const fn = a => {return a};
⑤ 函数体内只要是一行代码,不管需要不需要return 均可以省略{}。 返回值为一行代码的结果 。
const fn2 = a => console.log(a); //1
console.log(1) //1
console.log(fn2(1)) //undefined
const fn3 = a => {return console.log(a)} //3
console.log(fn3(3)) //undefined
⑥ 如果只有一行代码,代码的内容为返回的是一个对象,那么{}可以省略,对象要用()包裹。
const fn5 = function() {
return {
a:1,
b:2
}
} // 转换为箭头函数
const fn5 = () => ({a:1,b:2});
console.log(fn5()) //{a:1,b:2}
箭头函数省略写法:
–省略 () 的条件:只有一个参数,多个参数和没有参数都不能省略。
–省略 {} 的条件:函数体内只有一条表达式语句,且该表达式的计算结果作为返回值,省略 {} 和 return。
2.2 箭头函数没有 arguments,可以使用 rest 参数
//普通函数
let arguments = 3;
function fn() {
console.log(arguments) //伪数组 1,2,3,4
}
fn(1,2,3,4)
//箭头函数
let arguments = 3;
const fn = () => {
console.log(arguments) //3
}
fn(1,2,3,4)
2.3 注意事项-this
如果对象当中的属性是箭头函数,箭头函数当中的this,并不是调用者。而是定义对象时的环境this.
var color = 'green';
const obj = {
color:'red',
// // 普通函数
fn:function() {
console.log(this.color) //red
// 箭头函数
fn:() => {
console.log(this,this.color) //this指向window,green
}
}
}
obj.fn();
箭头函数当中的this是定义函数时的this,与调用无关。(箭头函数自身不会绑定this)
function Box() {
this.color = 'blue';
this.btn = document.querySelector('button');
this.init();
}
Box.prototype.init = function() {
var _this = this; // 如果使用箭头函数,该语句可以省略
// 普通函数
this.btn.onclick = function() {
console.log(this.color) //undefined
}
// 箭头函数
this.btn.onclick = () => {
console.log(this.color); //blue
}
}
const Box = new Box();
总结:
①箭头函数当中的this并不是运行时的this,而是定义时的this。换言之,箭头函数当中不会绑定this。
② this指向到外围的作用域。如果是对象当中的箭头函数,那么this指向对象的外围 。
2.4 注意事项-call-apply
箭头函数,通过call,apply来执行,无法更改this。通过bind无法绑定this.
var color = "blue";
const obj = {
color:"red",
run:function () {
console.log(this.color);// blue;
},
run2:()=>{
console.log(this.color);// blue
}
}
obj.run.call(window);
// 将run下的this指向到window,运行该函数。
obj.run2.call(obj);
// 将run2下的this指向到obj,运行该函数。箭头函数通过call,apply是无法设置this.
obj.run2.apply(obj);
// 将run2下的this指向到obj,运行该函数。箭头函数通过call,apply是无法设置this.
const fn = obj.run2.bind(obj);
// 将run2当中的this指向到obj,并将指向到obj的函数进行返回。箭头函数通过bind设置无效。
fn();
2.5 注意事项-构造函数
// 普通函数
function Box() {
}
const Box = new Box();
// 将箭头函数作为构造函数
const Box = () => {
}
const Box = new Box();// TypeError: Box is not a constructor
console.log(Box.prototype);//undefined
3 对象的简写和合并
ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁
3.1 对象的简写
函数的简写
const obj = {
// 普通函数
fn:function() {
},
// 箭头函数
fn1:() => {
},
// 简写:function 可以省略,是对普通函数的简写。 不要将fn2作为构造函数。
fn2() { // obj.fn2 is not a constructor
console.log(arguments)
}
}
const my = new obj.fn2();
console.log(obj.fn2.prototype);//undefined
属性表达式
const c = 3;
const obj = {
a:1,
b:2,
c, //省略不写。简化
[c]:4 // 将c的值作为属性名。 定义直接设置。
}
obj[c+10] = 50; //{3:4,13:50,a:1,b:2}
console.log(obj) //{3:4,a:1,b:2}
3.2 通过扩展运算符合并对象
① 通过扩展运算符(…)将对象进行合并
② 进行的是展开运算。spread:将对象或数组当中的值以,进行分割并进行展开
③ 将obj与obj2进行合并,不会对obj与obj2造成影响
const obj = {
a:1,
b:2
}
const obj2 = {
c:3,
d:4,
}
const obj3 = {
...obj,// a:1,b:2
...obj2// c:3,d:4
}
console.log(obj3,obj,obj2); //{1,2,3,4} {1,2} {3,4}
④ 如果合并的两个对象中的属性名字相同,那么以最后一个合并对象为准
const obj5 = {
g:4,
...obj,// a:1,b:2
x:8,
...obj2,//c:3,d:4
e:5,
a:1000
}
3.3 通过Object.assign合并对象
var obj = {
a:1,
b:2
}
var obj2 = {
c:3,
d:4
}
// 传递的参数不限:第一个参数是目标对象,其余的参数为源对象
const obj3 = Object.assign(obj,obj2);
// 将obj2合并到obj当中,然后将obj进行返回
console.log(obj,obj2); //{a: 1, b: 2, c: 3, d: 4} {c: 3, d: 4}
console.log(obj3 === obj);// true
4 解构赋值
ES6允许按照一定模式从数组和对象中提取值,对变量进行赋值,这被称为解构赋值
4.1 数组解构赋值
① 基本写法:解构目标=解构源
const a = 1;
const b = 2;
// 上面的写法通过数组的解构赋值也可以
let [a,b] = [1,2]; // 一一对应。将1赋值给a,将2赋值给b
console.log(a,b); //1 2
// 将a与b的值进行互换:
[a,b] = [b,a];// [a,b] = [2,1]
console.log(a,b); //2 1
② 先定义数组再进行解构
const arr = ["刘备","关羽","张飞"];
const [a,b,c] = arr;// [a,b,c] = ["刘备","关羽","张飞"]
console.log(a,b,c);// 刘备 关羽 张飞
③ 忽略
const [a, ,c] = ["A","B","C"];
console.log(a,c);// A C
④嵌套
const [a,b,[c,d,[e,f]]] = [1,2,[3,4,[5,6]]];
console.log(a,b,c,d,e,f) //1,2,3,4,5,6
⑤ 默认值
const [a,b,c] = [1,2];
console.log(a,b,c);// 1 2 undefined
const [a,b,c=9] = [1,2];// 默认值使用=
console.log(a,b,c);// 1 2 9
const [a,b,c=10] = [1,2,3];
console.log(a,b,c);// 1 2 3
const [a,b,c=7] = [1,2,undefined];
// 如果解构的值是undefined等同于未传递参数 [1,2,undefined] 相当于 [1,2]
console.log(a,b,c);// 1 2 7
⑥ 剩余运算符 …
// 收集
const [a,b,c,...d] = [1,2,3,4,5,6,7,8];
console.log(a,b,c,d);// 1 2 3 [4,5,6,7,8]
⑦ jsdom
const divs = document.querySelectorAll("div");
console.log(divs)
const [one,two] = divs;
console.log(one,two); // <div>1</div> <div>2</div>
const [...three] = divs;
console.log(three); //[div,div]
⑧ 字符串
const [a,b,c,d] = "一会下课了";
console.log(a,b,c,d);//一会下课
注意:
- 数组的结构赋值本质上是按照索引进行解构。
- 等号的右边必须是数组或者其他可遍历对象,否则报错!
4.2 对象解构赋值
对象解构赋值 右侧的解构源是对象
① 基本写法
const {a:a,b:c,d:e} = {a:1,b:2,d:3};
// 定义的变量或常量在等号左侧对象的冒号右边。
console.log(a,c,e);// 1 2 3
const {a,b,c} = {a:1,b:2,c:3};
// 相当于 const {a:a,b:b,c:c} = {a:1,b:2,c:3};
console.log(a,b,c);// 1 2 3
② 顺序不用一一对应
const {a,c,d} = {d:1,a:3,c:5};
console.log(a,c,d);// 3 5 1
③ 避免冲突
const a = 100;
const obj = {a:1,b:2};
const {a:my,b} = obj;// my接收右侧的属性名字为a的属性值
console.log(my,b);// 1 2
④ 默认值
const {d,a,b,c} = {a:1,b:2,c:3};
// 当解构源没有对应值时,那么接收到的是undefined
console.log(a,b,c,d);// 1 2 3 undefined
const {a,b,c=3} = {a:1,b:2};
console.log(a,b,c);// 1 2 3
⑤ 剩余运算符
const {a,b,c,...d} = {a:1,b:2,c:3,d:4,f:5};
console.log(a,b,c,d);// 1 2 3 {d:4,f:5}
⑥ 混合复杂 (数组结构与对象结构)
const {a,b,c:[x,y,z]} = {a:1,b:2,c:[4,5,6]}
console.log(a,b,x,y,z);// 1 2 4 5 6
⑦ 复杂解构
let wangfei = {
name: '王菲',
age: 18,
songs: ['红豆', '流年', '暧昧', '传奇'],
history: [
{name: '窦唯'},
{name: '李亚鹏'},
{name: '谢霆锋'}
]
};
// let {songs: [one, two, three], history: [first, second, third]} = wangfei;
// console.log(one,two,three); // 红豆 流年 暧昧
// console.log(first, second, third);// {name: '窦唯'} {name: '李亚鹏'} {name: '谢霆锋'}
let {songs: [one, two, three], history: [{name}, {name:secondName}, {name:thirdName}]} = wangfei;
console.log(name,secondName,thirdName);// 窦唯 李亚鹏 谢霆锋
4.3 解构赋值用于函数传参
解构赋值可以用于声明变量的时候赋值或者修改变量值的时候赋值,还可以用于函数传参(给形参赋值)
数组解构赋值和对象解构赋值两种形式都可以
// 数组的解构赋值
// 定义函数
function fn([a, b]) {
console.log(a + b);
}
// 调用函数
fn([100, 200]); // 300
fn('helloworld'); // he
console.log('');
// 对象解构赋值
function func({name, address, age}) {
console.log(name, age, address);
}
func([10,20,30]); // undefined undefined undefined
func({name:'案例', age: 10, address:'上海'}); // 案例 10 上海
console.log('');
// jQuery 插件的参数形式
// 对象解构赋值
function player({
palyEle,
ducration=5000,
auplay=true
}) {
}
player({
playEle: '#Box',
duration: 3000
});
4.4 解构赋值应用场景
① 交换两个变量的值
let num1=100,num2=200;
console.log(num1, num2);
// 交换
[num1, num2] = [num2, num1];
console.log(num1, num2);
② 提取json数据中的一部分数据
// 假设 jsonDat 是使用 JSON.parse()解析
let jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
};
// 以前的方式
//var id = jsonData.id;
// 解构赋值的方式 提取 id 和 status
let {id, stauts} = jsonData;
③ 结构赋值接收函数的返回值
function fn() {
return [100,200,300]
}
let arr = fn(); // arr 是数组
let [a,b] = fn(); // 只提取返回值中前两个成员
console.log(arr);
console.log(a,b);
④ 在模块化中的应用
const { SourceMapConsumer, SourceNode } = require("source-map");
5 模板字符串
5.1 模板字符串
使用反引号声明的字符串,称之为“模板字符串”
模板字符串适合内容比较多且有格式或者需要嵌套很过变量的字符串。
const str = "abc";// 双引号
const str1 = 'abc';// 单引号
const str2 = `abc`;// 反引号,称为模板字符串。
console.log(str === str2,str2 === str1);// true true
① 字符串中可以出现换行符
const str = "下\n课";
console.log(str);
const str = `下
课`;
console.log(str);
② 字符串模板 支持变量常量 ${}:占位符
设置一个常量
const num = 1;
const str = `
<div>
<p>${num}</p>
</div>
`
const root = document.querySelector("#root");
root.innerHTML = str;
设置多个
const siteName = "百度";
const siteUrl = "http://www.baidu.com";
const str = `
<div>
<a href="${siteUrl}">${siteName}</a>
</div>
`
const root = document.querySelector("#root");
root.innerHTML = str;
表达式方法
const sex = 2;
const userName = "师法魔级超";
const str = `
<div>
${sex===1?"男":"女"}
<p>${userName.split("").reverse().join("")}</p>
</div>
`
const root = document.querySelector("#root");
root.innerHTML = str;
数组
const arr = [`<h1>1</h1>`,`<h2>2</h2>`,`<h3>3</h3>`,`<h4>4</h4>`];
const siteArr = [
{
siteName:"京东",
siteUrl:"http://www.jd.com"
},{
siteName: "腾讯",
siteUrl: "http://www.qq.com"
}
]
// const newSiteArr = siteArr.map(function (item) {
// return `
// <a href="${item.siteUrl}">${item.siteName}</a>
// `
// })相当于如下
const newSiteArr = siteArr.map(item => `<a href="${item.siteUrl}">${item.siteName}</a>`)
console.log(newSiteArr);
siteArr.map(item=>`<a href="${item.siteUrl}">${item.siteName}</a>`)
const str = `
<div>
${arr.join("")}
${siteArr.map(item=>`<a href="${item.siteUrl}">${item.siteName}</a>`).join("")}
</div>
`
const root = document.querySelector("#root");
root.innerHTML = str;
相对于单引号和双引号声明的字符串具有如下特性:
- 在模板字符串中换行会作为字符串内容, 单双引号字符串中换行会被当做语句结束。
- 模板字符串可以使用
${}
嵌入变量,表达式都可以嵌入到模板字符串中,${表达式}
。
其他特性与单双引号声明的字符串就一致的,比如模板字符串中写反引号也要转义。
5.2 字符串扩展
5.2.1 封装函数,该函数返回当前的时间
function getNowTime(){
const time = new Date();
return time.getFullYear()+
"-"+(time.getMonth()+1).toString().padStart(2,"0")+
"-"+time.getDate().toString().padStart(2,"0")+
" "+time.getHours().toString().padStart(2,"0")+
":"+time.getMinutes().toString()js.padStart(2,"0")+
":"+time.getSeconds().toString().padStart(2,"0");
}
console.log(getNowTime());//2021-04-27 21:27:57
5.2.2 字符串新增方法
ES3 方法:
indexOf()
lastIndexOf()
slice()
substring()
substr()
split()
toupperCase()
toLowerCase()
charCodeAt()
ES5 方法:
trim() 去除字符串两端的空格,全是空格的字符串会处理成空字符串。
ES6+ 新增方法
repeat() 字符串重复,参数指定重复的次数
includes() 判断字符串中是否包含某个值,返回布尔值;第二个参数可以指定开始查找的位置。
startsWith() 判断字符串是否以某个值开头,返回布尔值;第二个参数可以指定开始查找的位置。
endsWith() 判断字符串是否以某个值结尾,返回布尔值;第二个参数可以指定开始查找的位置。
padStart() 将字符串补全为指定的长度,将填充内容补到前面;第一参数指定目标长度,第二个参数指定填充内容(不指定默认空格) (ES2017)
padEnd() 将字符串补全为指定的长度,将填充内容补到后面;第一参数指定目标长度,第二个参数指定填充内容(不指定默认空格) ES2017)
trimstart() 去除字符串左边的空格。 (ES2019)
trimEnd() 去除字符串右边的空格。 (ES2019)
5.2.3 字符串功能演示
① padStart:左侧补全
const str = "1";// ababababa1
console.log(str.padStart(2));// 将长度设置为2,左侧被空格
console.log(str.padStart(2,0));// 01 将长度设置为2,左侧被0补充
console.log(str.padStart(2,"a"));// a1 将长度设置为10,左侧被a补充
② padEnd:右侧补全
console.log(str.padEnd(2));// 右侧添加一个空格
console.log(str.padEnd(2,"0"));//10
console.log(str.padEnd(2,"a"));//1a
③ repeat:重复
6 运算符
6.1 spread扩展运算符
-
扩展运算符(spread)也是三个点(…)。
-
它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列,对数组进行解包。
-
…: 将对象或数组以逗号进行分割然后展示。
-
spread扩展运算符:在等号右侧。在函数中,用于实参中。
合并对象
const obj = {
a:1,
b:2
}
const obj2 = {
c:3
}
const obj3 = {
...obj,// a:1,b:2
...obj2// c:3
}
合并数组
const arr = [1,2,3];
const arr2 = [4,5,6];
const arr3 = arr.concat(arr2);
console.log(arr,arr2,arr3);
//相当于
const arr4 = [
...arr,// 1,2,3
...arr2// 4,5,6
]
console.log(arr,arr2,arr4);
dom
const divs = document.querySelectorAll("div");
console.log(divs);
console.log([...divs][0])
const arr = [1,2,3,4];
function fn(a,b,c,d){
console.log(a,b,c,d);// 1,2,3,4
}
fn(...arr);//1,2,3,4
6.2 rest运算符
数组解构赋值
const [a,b,c,...d] = [1,2,3,4,5,6,7,8,9];
console.log(a,b,c,d);// 1 2 3 [4,5,6,7,8,9]
对象解构赋值
const {a,b,...c} = {a:1,b:2,c:3,d:4}
console.log(a,b,c);// 1,2 {c:3,d:4}
function fn(a,b,...nums){// [1,2,3,4,5]
console.log(a,b,nums);// 1 2 [3,4,5]
}
fn(1,2,3,4,5);
7 es6模块化 es6 module
概念及特点
模块化:将一个大项的项目,根据不同的功能将其拆分成若干个模块,该行为称为模块化。
模块:即是项目当中的一个文件(.js)。在该模块中可以定义一些变量,常量,函数。
操作模块:
1、如何将定义的变量,常量 ,函数如果导出。 如何导出。(暴露)
2、如果引入模块导出的变量,常量 ,函数。 如何导入。 (依赖)
优点:
1、可以大大降低命名冲突问题
2、功能具体化,减少后期维护成功,提升程序重复使用率。
3、可以提升程序执行效率。
特征:
1、模块之间可以相互依赖。
封装模块
封装模块:将相对应的功能放置到一个
JS
当中,那么称该JS
为我封装某功能的某块。 1、封装的完成之后,尽量不要做修改的操作。 如果要做修改,那么建议重新创建一个版本。
2、将一些重复使用的,做为封装模块。
7.1、导出采用直接导出的形式
- 如何导出
// 导出一个变量
export let userName = "laotie";
// 导出一个常量
export const age = 80;
// 导出一个函数
export const fn = function () {
console.log("你好");
}
-
如何引入
-
引入采用import {} from "./mo/mo.js"
from 后面是导入的地址.
{}当中的名字,必须要与导出的名字一致。
// from 后面是导入的地址. {}当中的名字,必须要与导出的名字一致。
import {userName,age,fn} from "./mo/mo1.js"
console.log(userName,age,fn);
7.2、引入多个模块时,名字冲突,可以通过别名as(alias)解决。
- 如何导出
// mo1.js与mo2.js是可以导出相同名字的数据的。
// mo1.js
// 导出一个变量
export let userName = "laotie";
// 导出一个常量
export const age = 80;
// 导出一个函数
export const fn = function () {
userName = "laozhang";
console.log("你好",userName);
}
// mo2.js
export let userName = "xiaowang";
export let age = 13;
- 如何引入:as
import {userName as myUserName} from "./mo/mo1.js";
import {userName} from "./mo/mo2.js"
console.log(userName);// xiaowang
console.log(myUserName);// xiaowang
7.3、如果引入的模块当中暴露出来的数据较多时,引入时可以采用 * as
- 如何导出
// mo-->mo3.js
export let a = 1;
export let b = 2;
export let c = 3;
export let d = 4;
export let e = 5;
export let f = 6;
export let userName = "laoli";
export let age = 12;
export let sex = "男";
- 如何引入:*as
import * as mo3 from "./mo/mo3.js";
console.log(mo3.a,mo3.b);// 1 2
7.4、先定义再导出
- 如何导出
// mo->mo4.js 先定义,再导出
let a = 1;
let b = 2;
const c = 3;
const fn = function () {
console.log("fn");
}
export {
a as aa,// 别名
b,
// g:xx;// 不支持
c,
fn
}
- 如何引入
import {aa,b,c,fn} from "./mo/mo4.js";
console.log(aa,b,c);// 123
fn();// fn
7.5、默认导出(使用频率比较高)
- 如何导出
let a = 1;
let b = 2;
// 通过export default的形式导出,称为默认导出。
// export default 只允许写一次。
// export default {
//
// }
export default {
c:3,
f:6,
a,
b,
fn(){
// this 为当前对象
console.log("fn运行啦",this);
},
fn2:()=>{
console.log("fn2运行啦",this);// undefined
}
}
- 如何引入:自定义名字代替{},export default 只允许写一次
import demo from "./mo/mo5.js";
console.log(demo.a,demo.b,demo.c,demo.f);
// demo.fn();// this--->demo 1,2,3,6
// demo.fn2();// this--->undefined
// const fn = demo.fn;
// fn();// --->undefined
7.6、混合导出
将直接导出与默认导出一起使用,称为混合导出
- 如何导出
// 将直接导出与默认导出一起使用,称为混合导出
export let d = 5;
export default {
a:1,
b:2,
c:3
}
- 如何引入
- suibian接收的是默认导出的对象,{d} 直接导出
import suibian,{d} from "./mo/mo6.js";
console.log(d,suibian.a,suibian.b,suibian.c);// 5 1 2 3
7.7 嵌套mo
- 如何导出
// mo7.js 依赖mo5.js
import mo5, {a, b} from "./mo5.js";
export default {
a,
b,
// c:mo5.c
...mo5,
goods:"xxxx"
}
- 如何引入
import mo7 from "./mo/mo7.js";
import mo5 from "./mo/mo5.js";
console.log(mo7); //mo05.js执行啦 object:1236
8 class类
-
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。
-
通过class关键字,可以定义类。基本上,ES6 的class可以看作只是一个语法糖,
-
它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、
-
更像面向对象编程的语法而已。
8.1 基本用法
function fn() {
}
const fn2 = function() {
}
定义类
let Box = class {
}
class Box {
}
类的语法特性:
- 类本质上还是个构造函数,使用 typeof 返回
function
。 - 类只能用于实例化,无法被调用的!
- 类中只能定义属性和方法,其他语句都不能写,不能直接使用 this。
- 类中定义的属性会添加到实例本身,类中定义的方法添加到实例的原型上。
8.2 类的特点
① 类的本质就是一个function
class Box {
}
console.log(typeof Box);//function
② 类也是有原型的
class Box {
}
console.log(Box.prototype);//{constructor:f}
const Box = new Box;
console.log(Box.__proto__ === Box.prototype);//true
console.log(Box.__proto__.constructor === Box);//true
console.log(Box.prototype.__proto__ === Object.prototype);//true
console.log(Object.prototype.__proto__) //null
class Box {
}
Box (); //语法错误
④ 类不允许重复定义
class Box {};
class Box {};//语法错误
⑤ class不会被提升
const obj2 = new Box() //提升后报引用错误
class Box{};
const obj = new Box()
⑥ 实例化类时,默认执行constructor函数(构造器函数)
class Box {
constructor() {
console.log(this) ;//Box
this.userName = "laowang";
this.age = 18;
}
}
const obj = new Box()
console.log(obj) // Box{}
构造器方法在类被实例化的时候,会自动调用;可以接受类名后面()里的参数。
构造器方法中的 this 指向实例。
一般用于实例化的时候为属性赋值。
⑦ 允许写constructor,允许写函数,函数之间没有逗号,函数不要用function声明js语句可以写在函数体内。在constructor同级当中定义的函数全部都在原型当中。用等于号赋值的在实例当中。
- 写在实例中的行为
class Box {
constructor() {
this.userName = "laowang"; //实例中
this.fn = function() { //实例中
}
}
sex = '男' //实例中
hobby = function() { //实例中
}
}
const obj = new Box();
console.log(obj)
- 写在原型中的行为
class Box {
one() { //原型中
}
two() { //原型中
}
}
Box.prototype.three = function() {
//不行改动原来封装好的函数,可在原型中修改
} //原型中
Object.assign(Box.prototype,{
four() {
} //原型中
})
console.log(new Box());
8.3 关于原型
① 原型是共享的
每次new,都会重新生成一个对象(this),但是他们的原型都是相等的。(原型的特点共享)
class Desk {
}
const obj = new Desk();
const obj2 = new Desk();
console.log(obj === obj2);//false
console.log(obj.__proto__ === obj2.__proto__);// true 说明原型是共享的
② 实例化时可以传递参数
将1,2传递给constructor函数当中的a,b.
先去实例中找,再去原型中找,与构造函数相同,如果实例当中拥有该属性,那么不会到原型对象中去查找。
class Box {
constructor(a,b) {
this.a = a;
this.b = b;
this.sum = 3;
}
sum () {
console.log('sum执行了')
}
}
const obj = new Box(1,2)
console.log(obj.sum) //3
注意
不要通过____protot____去更改值。
原型对象:写在原型当中的
实例对象:在构造函数内this.xxx,xx=xxx
class Box{
constructor(a,b) {
this.a = a;
this.b = b;
}
sum(){
return this.a + this.b;
}
}
const obj = new Box(1,2);
console.log(obj.sum());// 3
const obj2 = new Box(3,4);
console.log(obj2.sum());// 7
console.log(obj.__proto__ === Box.prototype);// true
console.log(obj2.__proto__ === Box.prototype);// true
console.log(obj2.__proto__ === obj.__proto__);// true
8.4 对属性进行读写的控制:get set
get不允许单独设置,有get必须有set,但是有set可以没有get
class Box {
constructor() {
this.userName = "laowang";
}
// 当读取userName属性时,会执行该函数。get 不允许单独设置
get userName() {
console.log('get执行了');
return 1; // get的返回值,即是读取的值
}
set userName(value) {
console.log("set执行啦",value);// laowang
}
}
const obj = new Box();
console.log(obj.userName);
console.log(obj);//先执行set,再执行get,Box
class Box {
constructor() {
this.userName = "laowang";
}
get userName(){
return this._userName;
}
set userName(value){
this._userName = value;
}
}
const obj = new Box();
console.log(obj.userName);// zlaowang
obj.userName = "wangwu";
console.log(obj.userName);// wangwu
8.5 继承
子类继承了父类之后,子类的实例,不但可以使用自己的类定义的属性和方法,还可以使用父类上定义的属性和方法。
**单继承:**一个父类可以有多个子类,但是一个子类只能有一个父类。
① extends 继承类的关键字
extends和super结合使用,super调用父级的constructor,必须放在this的前面
class Father{
constructor() {
this.userName = "父类";
}
run(){
console.log("我是run");
}
}
class Child extends Father {
constructor() {
super(); // 调用父级的constructor,必须要写在this的前面
this.str = "子类"
}
fn() {
console.log("我是fn")
}
}
const obj = new Child();
console.log(obj) //child {父类,子类}
obj.fn(); //我说fn
obj.run(); //我是run
② super关键字
子类如果重写构造器方法,必须在里面同 super()
调用父类的构造器方法。
super()
只能在子类构造器方法中使用,且super()
前面不能有其他代码。
在原型中自动补全constructor super 子类调用父类,调用父级的函数
class Box {
constructor(a,b) {
console.log(a,b);// 1 2
this.a = a;
this.b = b;
}
fn(){
console.log("Box->fn");
}
}
class Desk extends Box{
// constructor(a,b) {
// super(a,b);
// }
fn(){
// console.log("Desk->fn");
super.fn();// 调用父级的函数。
}
}
const obj = new Desk(1,2);
obj.fn(); //1 2 Box->fn
new指向子类中的super,super指向父类的constructor
8.6 静态方法
在类中的方法前面加static
关键字,就表示该方法不会添加到实例的原型上,而是添加到类本身上,被称为静态方法。
①function
Box.a Box.b是静态属性,Box.fn Box.run是静态方法
function Box() {
}
Box.a = 1;
Box.b = 2;//静态属性
Box.fn = function() {
console.log("fn",this.a,this.b) //fn 1 2 Box
} //静态方法
Box.run = () => { //静态方法
console.log("run".this) //window
}
console.log(Box.a,Box.b);
Box.fn()
Box.run() //undefined
② class
用处:一般可以写一些工具类(函数)
写法一:静态方法属性,通过实例化时无法获取。js 字面量 const Box = {a:1,b:2,fn(){}}
//Box.a Box.b为静态属性 Box.fn 为静态方法
class Box{};
Box.a = 1;
Box.b = 2;
Box.fn = function () {
console.log("fn")
}
console.log(Box.a,Box.b);
Box.fn(); //fn
const obj = new Box();
console.log(obj.a,obj.b,obj.fn);// undefined undefined undefined
写法二:使用satatic关键字
class Box {
static a = 1;
static b = 2;
static run() {
console.log("run",this.a,this.b);
}
static fn() {
console.log("fn ",Box.a,this.b)
}
}
console.log(Box.a,Box.b); //1 2
Box.run(); //run 1 2
Box.fn(); //fn 1 2
③ 特点:继承
静态方法是可以继承的
class Box {
static run() {
console.log("run")
}
static fn() {
console.log("Box-->fn")
}
}
class Desk extends Box {
static fn() {
console.log("fn-->Desk",this === Desk)
}
}
Desk.run();//run
Desk.fn(); //fn-->Desk true
8.7 访问器属性
// 创建类
class Person {
// 定义属性
firstName = null;
lastName = null;
// 构造器方法
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
// 访问器属性 读取
get fullName() {
return this.firstName + '·' + this.lastName;
}
// 访问器属性 设置
set fullName(val) {
// 判断值是否符合规范
if (!val.includes('·')) {
return;
}
this.firstName = val.split('·')[0];
this.lastName = val.split('·')[1];
}
}
案例 实现切换
class Tag {
constructor() {
this.btns = document.querySelectorAll(".demo button");
this.divs = document.querySelectorAll('.demo div');
this.colorArr = ['pink','orange','yellowgreen']
this.index = 0;
this.init();
}
// 初始化
init() {
this.btns.forEach((item,index) => {
console.log(this.btns[this.index]) //索引的button
item.onclick = () =>{
console.log(item)// button
// 清除之前点击的内容
this.btns[this.index].style.background = "";
this.divs[this.index].style.background = "";
this.divs[this.index].style.display = 'none';
// 将定义的索引赋值
this.index = index;
// 增加点击效果
this.btns[this.index].style.background = this.colorArr[this.index];
this.divs[this.index].style.background = this.colorArr[this.index];
this.divs[this.index].style.display = "block";
}
this.divs[index].style.display = 'none'
})
this.btns[this.index].style.background = this.colorArr[this.index];
this.divs[this.index].style.background = this.colorArr[this.index];
this.divs[this.index].style.display = "block";
}
}
new Tag();
优化hide,show
init(){
this.btns.forEach((item,index)=>{
// 为按钮增加事件
item.onclick = ()=>{
this.hide();
this.index = index;
this.show();
}
this.divs[index].style.display = "none";
})
this.show();
}
hide(){
this.btns[this.index].style.background = this.divs[this.index].style.background = "";
this.divs[this.index].style.display = "none";
}
show(){
this.btns[this.index].style.background = this.divs[this.index].style.background = this.colorArr[this.index];
this.divs[this.index].style.display = "block";
}
}
使用set,get
// 初始化
init(){
this.btns.forEach((item,index)=>{
// 为按钮增加事件
item.onclick = ()=>{
this.index = index;// 0
}
this.divs[index].style.display = "none";
})
this.show();
}
hide(){
this.btns[this.index].style.background = this.divs[this.index].style.background = "";
this.divs[this.index].style.display = "none";
}
show(){
this.btns[this.index].style.background = this.divs[this.index].style.background = this.colorArr[this.index];
this.divs[this.index].style.display = "block";
}
get index(){
return this._index;
}
set index(i){// 1 0
if(this._index === undefined){
this._index = i;
return;
}
this.hide();// 隐藏
this._index = i;// 2 0
this.show();// 显示
}
}
9 Promise
9.1 Promise 概述
① promise的定义
抽象表达:
-
Promise是一门新的技术(ES6规范)
-
Promise是JS中进行异步编程的新解决方案
备注:旧方案是单纯使用回调函数
具体表达:
const p1 = new Promise(function(resolve,reject) {
setTimeout(() => {
resolve (1);// 将状态更改为成功(fulfilled),接收的参数是成功的值 1
reject(2);//将状态更改为失败(rejected),接收的参数是失败的值 2
},1000)
})
p1.catch(err => {
console.log(err);
})
console.log(p1)
② promise的状态
promise 对象具有三种状态:
pending (进行中) resolved (已成功) rejected (已失败)
-
pending变为resolved (fulfilled)
-
pending变为rejected
说明: 只有这2种, 且一个promise对象只能改变一次
无论变为成功还是失败, 都会有一个结果数据
成功的结果数据一般称为value, 失败的结果数据一般称为reason
promise 内部会封装一个异步操作,当异步操作完成, promise 的状态就会发生改变! 如果异步操作是成功的,状态变为 resolved,如果异步操作是失败的,状态变为 rejected。
当 promise 的状态发生改变,让promise的回调函数进行回调队列,等待执行。
没有 promise 的时候,异步操作完成直接让自己的回调函数进入回调队列等待执行;有了 promise 的封装,异步操作执行完毕,改变 promise 的状态,promise 状态改变让 promise 的回调函数进入回调队列等待执行。所以,promise 就是中间商!
const p1 = new Promise((resolve,reject) => {
setTimeout(() =>{
//resolve(1);
reject("err");
})
})
// 成功的结果数据一般称为value, 失败的结果数据一般称为reason
// 用于接收结果:then 函数接收两个参数(函数),如果p1的状态成功,那么执行第一个参数,失败执行第二个参数。
p1.then(function(value) {
console.log("成功",value); //成功 1
},function(reason) {
console.log("失败",reason); //失败 err
})
③ promise的基本流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-knsceUZ7-1620800127197)(C:\Users\王秀\AppData\Roaming\Typora\typora-user-images\image-20210429115743623.png)]
封装ajax
// xhr 基本封装:
function ajax(src){
return new Promise(function (resolve,reject) {
const xhr = new XMLHttpRequest();
xhr.open("get",src);
xhr.send();
xhr.onload = function () {
resolve(xhr.responseText);// 状态更改为成功,值为xhr.responseText
}
})
}
// 相当于
const p2 = new Promise(function (resolve,reject) {
const xhr = new XMLHttpRequest();
xhr.open("get","./03-异步问题.html");
xhr.send();
xhr.onload = function () {
resolve(xhr.responseText);
// 状态更改为成功,值为xhr.responseText
}
})
return p2;
ajax("./03-异步问题.html").then(value=>{
console.log(value);
})
ajax("./01-回调地狱.html").then(value=>{
console.log(value);
})
9.2 Promise 基本语法
① 使用 Promise 构造函数创建 promie 对象
let p = new Promise(() => {
});
p 就是创建的 promise 对象,初始状态是 pending
② 修改promise 对象的状态
// 需要 给Promie构造函数的参数(函数),传参数
let p = new Promise((resolve, reject) => {
// 调用 resolve,把状态改为 resolved
resolve();
// 调用 reject,把状态改为 reject
reject();
});
promise 对象的状态一旦修改,就不会再改变了。
Promise 构造函数的参数(回调函数)是同步执行的,在实例化的过程中就执行了。
通过 then 给 promise 对象指定的回调函数是异步执行的; 当promise状态发生改变,这里的回调函数进入回调队列等待执行!
③ 指定 promise 对象的回调函数
// 需要 给Promie构造函数的参数(函数),传参数
let p = new Promise((resolve, reject) => {
// resolve 和 reject ,不论谁先调用,调用之后状态就定了,后面再调用 promise 对象状态就不会改变了
// 调用 resolve,把状态改为 resolved, 该参数会传递给 then 第一个回调函数
resolve([199,200,23,234]);
// 调用 reject,把状态改为 reject , 该参数会传递给 then 第二个回调函数
// reject('连接超时...');
});
p.then((value) => {
// value 可以获取状态改为成功时,传递的数据
// value 就是调用 resolve 时给的实参
}, (reason) => {
// reason 可以获取状态改为失败是,传递的原因
// reason 就是 调用 reject 时给的实参
})
可以给 promise 指定两个回调函数,如果 promise 对象的状态是 resolved,调用第一个回调函数;如果 promise 状态是 rejected,调用第二个回调函数。
Promise 构造函数的回调函数中,调用 resolve 和 reject 的时候,可以传递参数; promise 对象的回调函数可以接收到!
④ 基本编码流程
// 1) 创建promise对象(pending状态), 指定执行器函数
const p = new Promise((resolve, reject) => {
// 2) 在执行器函数中启动异步任务
setTimeout(() => {
const time = Date.Now()
// 3) 根据结果做不同处理
// 3.1) 如果成功了, 调用resolve(), 指定成功的value, 变为resolved状态
if (time%2===1) {
resolve('成功的值 '+ time)
} else { // 3.2) 如果失败了, 调用reject(), 指定失败的reason, 变为rejected状态
reject('失败的值' + time)
}
}, 2000)
})
// 4) 能promise指定成功或失败的回调函数来获取成功的vlaue或失败的reason
p.then(
value => { // 成功的回调函数onResolved, 得到成功的vlaue
console.log('成功的value: ', value)
},
reason => { // 失败的回调函数onRejected, 得到失败的reason
console.log('失败的reason: ', reason)
}
)
⑤ 使用promise封装基于定时器的异步
function doDelay(timer) {
return new Promise((resolve,reject)=>{
setTimeout(()=>{
const time = Date.Now();
if(time%2===1){
resolve("奇数成功"+time);
}else{
reject("偶数失败"+time);
}
},timer)
})
}
doDelay(2000).then(value=>{
console.log(value);
},reason=>{
console.log(reason);
})
9.3 Promise的特点
3.1 指定回调函数的方式更加灵活
- 旧的: 必须在启动异步任务前指定
function run(cb){
// 异步
setTimeout(()=>{
cb(1);
},1000)
}
// run传递的为指定的回调函数。
run(function (v) {
console.log(v);// 1 先回调再执行
})
- promise: 启动异步任务 => 返回promie对象 => 给promise对象绑定回调函数(甚至可以在异步任务结束后指定/多个)
const p1 = new Promise(function (resolve,reject) {
// console.log("立即执行")
setTimeout(()=>{
resolve(1);//成功
// reject(-1);
},1000)
})
p1.then(value=>{
console.log(value);// 1
},reason => {
console.error(reason)
})
p1.then(value=>{
console.log(value);// 1
},reason => {
console.error(reason)
})
3.2 支持链式调用, 可以解决回调地狱问题 异常穿透
-
什么是回调地狱?
-
回调地狱的缺点?
不便于阅读
不便于异常处理
-
解决方案?
promise链式调用
new Promise((resolve,reject) => { setTimeout(() => { resolve("报名") },1000) }).then(value => { console.log(value) //报名 return new Promise((resolve,reject) => { setTimeout(() => { resolve("学习") },1000) }) }).then(value => { console.log(value) //学习 return new Promise((resolve,reject) => { setTimeout(() => { resolve("上班") },1000) }) }).then(value => { console.log(value) })
9.4 Promise 实例的方法
4.1 function构造函数
-
Promise构造函数: Promise (excutor) {}
(1) executor函数: 执行器 (resolve, reject) => {}
(2) resolve函数: 内部定义成功时我们调用的函数 value => {}
(3) reject函数: 内部定义失败时我们调用的函数 reason => {}
说明: executor会在Promise内部立即同步调用,异步操作在执行器中执行
new Promise((resolve,reject)=>{
console.log(111111111);
setTimeout(()=>{
resolve("success");// 成功
// reject();// 失败
},1000)
}).then(value=>{
console.log("resolve函数执行后,会运行我value")
},reason=>{
console.log("reject函数执行后,会运行我reason")
})
console.log(2222222222); //111 222 value
4.2 then 方法
① 参数
then 方法可以指定两个函数,都是回调函数; promise状态如果是成功的调用第一参数,promise 对象如果是失败的,调用第二个参数。
② 返回值
then() 一定会返回 promise 对象,所以 then 方法支持链式调用。
then() 方法返回值规则如下:
- 如果回调函数没有返回值,then 方法返回一个 成功状态的 promise 对象,值是 undefined。
- 如果回调函数指定返回值(且不是 promise 对象),then 方法返回一个成功状态的 promise 对象,值是回调函数的返回值。
- 如果回调函数中抛出错误,then 方法返回一个失败状态的promise对象,失败原因就是错误信息(自动传递)。
- 如果回调函数返回的是一个 promise 对象,then 方法的返回值就是这个 promise 对象。
const p1 = new Promise((resolve,reject) => {
})
const p2 = p1.then(value => {
},reason => {
})
console.log(p2);
4.3 catch 方法
① 参数
参数指定一个回调函数,promise对象的状态变为失败的时候会调用。
说明: then()的语法糖, 相当于: then(undefined, onRejected)
② then 和 catch 可以配合使用
let p = new Promise((res, rej) => {
// 修改为成功的状态
// res();
// 修改为失败的状态
rej('故意出错!');
});
let p1 = p.then(value => {
console.log('成功');
}).catch(reason => {
console.log('失败:', reason);
}); //失败,故意出错
③ 异常穿透
使用 then 进行链式调用的时候,可以只给then指定成功的回调函数,最后通过 catch 兜底,不论哪一步出现异常(状态变为失败),最终都会穿透到 catch ,执行它的回调函数。
function setTimeoutPromise(delay, value) {
// 创建 promise 对象并返回
return new Promise((resolve, reject) => {
setTimeout(resolve, delay, value);
});
}
setTimeoutPromise(2000).then(() => {
//取 0 -9 之间随机数
let rand1 = Math.floor(Math.random() * 10);
console.log(rand1);
a; // 异常语法
// 返回一个 promise 对象
return setTimeoutPromise(3000, rand1);
}).then(value => {
// 取随机数 + value
let rand2 = Math.floor(Math.random() * 10) + value;
console.log(rand2);
// 返回一个 promise 对象
return setTimeoutPromise(1000, rand2);
}).then(value => {
// 取随机数 + value
let rand3 = Math.floor(Math.random() * 10) + value;
console.log(rand3);
}).catch(reason => {
console.log('错误:', reason);
})
4.4 finally
接收一个回调函数作为参数,当promise对象状态发生改变,回调函数就调用,不论变为成功状态还是失败状态。
9.5 Promise 构造函数本身的方法
5.1 Promise.resolve()
① 功能
value: 成功的数据或promise对象
返回一个 promise 对象,可以用来快速创建一个 promise 对象
② 根据参数不同返回的promise也不同:
- 如果不给参数,返回一个成功状态的 promise,值是 undefined
const p1 = Promise.resolve();
console.log(p1); //undefined
const p2 = Promise.resolve(true);
console.log(p2) //true
- 如果参数是个 promise 对象,直接把这个 promise 对象作为返回值。
const p2 = Promise.resolve(true);
console.log(p2) //true
// 3 传递的是promise ,返回的就是promise
const p3 = new Promise((resolve,reject) => {
setTimeout(() => {
resolve("成功")
})js
})
const p4 = Promise.resolve(p3);
console.log(p4,p4===p3) //成功 true
具有 then 方法的就是 thenable 对象!
let obj = {
then(resolve, reject) {
reject('老子错了');
}
};
let p4 = Promise.resolve(obj);
// 执行 resolve 方法的时候,内部实例化 Promise 构造函数,把 then 方法作为参数
5.2 Promise.reject()
没有 Promise.resolve 那么复杂,就返回一个失败状态的 promie 对象,参数不论是什么数据都会作为返回的promise对象失败原因;没有参数失败原因就是undefined。
const p1 = Promise.reject();
console.log(p1); //undefined
// p1为失败的promise对象,值1
const p2 = Promise.reject(false);
console.log(p2); //false
// 如果传入的是一个成功状态的promise,那么p1值为该promise,状态失败
const p3 = Promise.resolve(2)//成功状态,值为2的promise对象
const p4 = Promise.reject(p3);
p4.catch(reason => {
// console.log(reason === p3)// true
reason.then(value=>{
console.log(value);// 2
})
})
5.3 Promise.all()
可以把多个 promise 对象整合成一个 promise 对象并返回。
参数是一个数组或者其他可遍历对象,成员都是 promise 对象(不是 promise 会自动使用 Promise.resolve 创建为 promise 对象)。
返回的 promise 对象的状态分为两种情况:
- 如果传入的 promise 对象全部都变为了成功状态,返回的 promise对象会等到所有的 promise 对象状态变化之后才改变状态,改为成功状态,值是数组,包含所有 promise 的值。
- 一旦有一个 promise 对象变为失败状态,返回的 promise 对象立即变为失败状态!
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
// const p3 = Promise.resolve(3);
const p3 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(3);
},2000)
})
const p = Promise.all([p1,p2,p3,4,5]);
console.log(p); //1 2 3 4 5
5.4 Promise.race()
promises: 包含n个promise的数组
说明: 返回一个新的promise, 第一个完成的promise的结果状态就是最终的结果状态
const p1 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(1);
},30)
})
const p2 = new Promise((resolve,reject)=>{
// setTimeout(()=>{
// reject(2);
// },10)
reject(2);
})
const p3 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(3);
},20)
})
const p = Promise.race([p1,p2,p3,4]);
console.log(p===p2);// false
// 返回一个新的promise.第一个执行完的promise的结果为最终的结果状态
console.log(p); //到 2 结束
案例 图片预加载
// 正常加载
for (let i = 1; i <= 74; i++) {
let img = document.createElement('img');
img.src = "./picture/"+i+".png";
img.width = "200";
document.body.appendChild(img)
}
// 预加载
let num = 0;
let imgArr = new Array(75);// [,,,,,img,,,,,,,,,,,,,img]
const div = document.querySelector("div");
for(let i = 0;i<imgArr.length;i++){
let img = document.createElement("img");
img.src = "./picture/"+(i+1)+".png";
img.width = "200";
img.onload = function () {
// console.log(i);
// 当图片加载完成后,会执行
num++;
imgArr[i] = img;
div.innerHTML = "已加载"+((num/imgArr.length)*100).toFixed(2)+"%";
if(num >= imgArr.length){
console.log("加载完毕");
imgArr.forEach(item=>document.body.appendChild(item));
}
// console.log(i);
}
img.onerror = function () {// 图片地址异常会执行该函数。
// console.log("error");
console.log(i);
img.src = "https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png"
}
}
通过promise加载
const div = document.querySelector("div");
let num = 0;
function getRandom(min,max){
return Math.floor(Math.random()*(max-min+1)+min);
}
function getImg(src){
return new Promise(function (resolve,reject) {
setTimeout(function () {
const img = document.createElement("img");
img.src = src;
img.width = "200";
img.onload = function () {
num++;
console.log(num);
// if(num>=74){
div.innerHTML = "已加载"+((num/74)*100).toFixed(2)+"%";
// }
resolve(img);
}
},getRandom(100,2000))
})
}
const promiseArr = [];
for(let i=1;i<=74;i++){
promiseArr.push(getImg("./picture/"+i+".png"));
}
// promiseArr 数组:[p1,p2,p3,p4] =====> 成功--》成功的回调--》 imgList [1,2,3,4]
Promise.race(promiseArr).then(img=>{
console.log(img);//
document.body.appendChild(img);
// imgList.forEach(img=>{
// document.body.appendChild(img);
// })
})
// Promise.all(promiseArr).then(imgList=>{
// imgList.forEach(img=>{
// document.body.appendChild(img);
// })
// })
9.6 promise 的几个关键问题
9.6.1 如何改变promise的状态?
(1) resolve(value): 如果当前是pending就会变为resolved
(2) reject(reason): 如果当前是pending就会变为rejected
(3) 抛出异常: 如果当前是pending就会变为rejected
const p1 = new Promise(function (resolve,reject) {
throw "异常";
})
console.log(p1);// 初始状态rejected 值 异常
9.6.2 一个promise指定多个成功/失败回调函数, 都会调用吗?
当promise改变为对应状态时都会调用
9.6.3 改变promise状态和指定回调函数谁先谁后?
(1) 都有可能, 正常情况下是先指定回调再改变状态, 但也可以先改状态再指定回调
(2) 如何先改状态再指定回调?
① 在执行器中直接调用resolve()/reject()
② 延迟更长时间才调用then()
(3) 什么时候才能得到数据?
① 如果先指定的回调, 那当状态发生改变时, 回调函数就会调用, 得到数据
② 如果先改变的状态, 那当指定回调时, 回调函数就会调用, 得到数据
9.6.4 promise.then()返回的新promise的结果状态由什么决定?
(1) 简单表达: 由then()指定的回调函数执行的结果决定
(2) 详细表达:
① 如果抛出异常, 新promise变为rejected, reason为抛出的异常
② 如果返回的是非promise的任意值, 新promise变为resolved, value为返回的值
③ 如果返回的是另一个新promise, 此promise的结果就会成为新promise的结果
9.6.5 promise如何串连多个操作任务?
(1) promise的then()返回一个新的promise, 可以开成then()的链式调用
(2) 通过then的链式调用串连多个同步/异步任务
9.6.6 promise异常传透?
(1) 当使用promise的then链式调用时, 可以在最后指定失败的回调,
(2) 前面任何操作出了异常, 都会传到最后失败的回调中处理
9.6.7 中断promise链?
(1) 当使用promise的then链式调用时, 在中间中断, 不再调用后面的回调函数
(2) 办法: 在回调函数中返回一个pendding状态的promise对象
10 async与await
10.1. mdn文档
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/async_function
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/awai
10.2 定义async
async function one() {
}
const two = async function() {
}
const obj = {
three:async function() {
},
four: async() => {
},
async five() {
}
}
10.3 async 函数
- 函数的返回值为promise对象
async function fn() {
}
console.log(fn());//成功的fulfilled undefined
// 返回的是非promise
async function fn1() {
return 1
}
console.log(fn1()); //成功的fulfilled 1
// 以上代码相当于:
Promise.resolve(1) //fulfilled 1
- promise对象的结果由async函数执行的返回值决定
async function fn2() {
return new Promise(function(resolve,reject) {
resolve("success")
})
}
console.log(fn2()) //fulfilled success
const p1 = new Promise(function(resolve,reject) {
reject('error')
})
async function fn3() {
return p1;
}
console.log(fn3()===p1) //false
**注意:**函数运行以后得到的是一个新的promise,状态与值同p1相同
// 异常
async function fn4() {
throw "异常"
}
console.log(fn4()) //rejected 异常
10.4 await 表达式
- await右侧的表达式一般为promise对象, 但也可以是其它的值
- 如果表达式是promise对象, await返回的是promise成功的值
async function fn1() {
const result = await new Promise(function(resolve,reject) {
resolve(100);
})
console.log(result) //100
// await右侧为成功的promise对象,那么return的返回值为100
}
fn1()
- 如果表达式是其它值, 直接将此值作为await的返回值
async function fn2() {
const num = await 99;
console.log(num) //99
}
fn2()
10.5 注意
async function fn() {
await 1;
}
- 如果await的promise失败了, 就会抛出异常, 需要通过try…catch捕获处理
async function fn3() {
try {
let str = await new Promise(function(resolve,reject) {
reject(0);
})
console.log(str)
} catch(e) {
console.log(e) //0
}
}
fn3()
练习
// 当右侧为成功promise,那么左侧得到的是成功的值。
function run(){
return Promise.resolve(1);
}
async function fn() {
const str = await run();
console.log(str);// 1
}
fn();
// 当右侧为失败promise,会产生异常,那么需要通过try---catch得到失败的值
function run1(){
return Promise.reject(1);
}
async function fn1() {
try{
const str = await run1();
console.log(str);
}catch (e) {
console.log(e);// 1
}
}
fn1();
// 当async中没有await关键词时。同步执行。
async function fn2() {
console.log(111111);
}
fn2();
console.log(2222222);
// 111111
// 222222
// 当async中有await关键词时。 await 右侧为非promise时,会暂停,先执行函数外的同步语句。
async function fn3() {
console.log(111111);
let str = await "你好";
console.log(str);// "你好"
}
fn3();
console.log(2222222);
// 111111
// 2222222
// 你好
// 当右侧为函数时,先执行函数同步代码,然后等待promise结果.执行函数外的同步语句。当 promise 的状态确定以后再执行await后续的程序。
function run2(){
console.log(3);
return new Promise((resolve,reject)=>{
console.log(4);
resolve(0);
})
}
async function fn4() {
let str = await run2();
console.log(str);// 0
}
fn4();
console.log(1);
// 3 4 1 0
解决回调地狱
function one() {
return new Promise(function(resolve) {
setTimeout(() => {
resolve(10)
}, 1000);
})
}
function two() {
return new Promise(function(resolve) {
setTimeout(() => {
resolve(20)
}, 1000);
})
}
function three() {
return new Promise(function(resolve) {
setTimeout(() => {
resolve(30)
}, 1000);
})
}
async function fn4() {
let oneResult = await one();
console.log(oneResult);
let twoResult = await two();
console.log(twoResult);
let threeResult = await three();
console.log(threeResult);
}
fn4()
11 JS异步之宏队列与微队列
11.1. 原理图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qRtdWQDP-1620800127201)(file:///C:\Users\王秀\AppData\Local\Temp\ksohtml14516\wps1.jpg)]
11.2 说明
-
JS中用来存储待执行回调函数的队列包含2个不同特定的列队
-
宏列队: 用来保存待执行的宏任务(回调), 比如: 定时器回调/DOM事件回调/ajax回调
-
微列队: 用来保存待执行的微任务(回调), 比如: promise的回调/MutationObserver的回调 (MutationObserver:会观察指定的DOM元素,当DOM元素发生变化时,会执行相对应的回调)。
-
JS执行时会区别这2个队列
(1) JS引擎首先必须先执行所有的初始化同步任务代码
(2) 每次准备取出第一个宏任务执行前, 都要将所有的微任务一个一个取出来执行
/* *同:1 2 3 *微:4 *宏:100 */ const btn = document.querySelector("button"); const div = document.querySelector("div") btn.onclick = function() { setTimeout(() =>{ console.log(100) }) console.log(1); div.innerHTML += "!"; div.className = "bg"; console.log(2) console.log(3) } const server = new MutationObserver(function(e) { // console.log("div的内容发生了变化",e) console.log(4) }) // 观察div内容是否发生了变化 server.observe(div,{ childList:true, attributes:true,//属性 })
面试题
/*
* 同:3
* 微:2 4
* 宏:1
* */
setTimeout(()=>{
console.log(1)
},0)
Promise.resolve().then(()=>{
console.log(2)
})
Promise.resolve().then(()=>{
console.log(4)
})
console.log(3)
/*
* 同:2 5
* 微:3 4
* 宏:1
* */
setTimeout(() => {
console.log(1)
}, 0)
new Promise((resolve) => {
console.log(2) //会立即执行,所以是同步的
resolve()
}).then(() => {
console.log(3)
}).then(() => {
console.log(4)
})
console.log(5)
/*
* 同 3 7 4
* 微 1 2
* 宏 5
* */
const first = () => (new Promise((resolve, reject) => {
console.log(3)
let p = new Promise((resolve, reject) => {
console.log(7)
setTimeout(() => {
console.log(5)
resolve(6);
}, 0)
resolve(1);//执行的是p下面的
})
resolve(2);//执行的是promise下面的
p.then((arg) => {
console.log(arg)
})
}))
first().then((arg) => {
console.log(arg)
})
console.log(4)
/*同:1 7
* 微:2 3 8 4 6 5
* 宏:0
* */
setTimeout(() => {
console.log("0")// 无
}, 0)
new Promise((resolve,reject)=>{
console.log("1")// 无
resolve()
}).then(()=>{
console.log("2")
new Promise((resolve,reject)=>{
console.log("3")
resolve()
}).then(()=>{
console.log("4")
}).then(()=>{
console.log("5")
})
}).then(()=>{
console.log("6")
})
new Promise((resolve,reject)=>{
console.log("7")// 无
resolve()
}).then(()=>{
console.log("8")
})
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MTb3ayEi-1620800127202)(C:\Users\王秀\AppData\Roaming\Typora\typora-user-images\image-20210507100046137.png)]
/*
* 同:4 1 3 6 8
* 微:2 7
* 宏:5
* */
async function async1() {
console.log(1);
await async2();
console.log(2);
}
async function async2() {
console.log(3);
}
console.log(4);
setTimeout(function () {
console.log(5);
}, 0)
async1();
new Promise(function (resolve) {
console.log(6);
resolve();
}).then(function () {
console.log(7);
});
console.log(8);
Function Promise(executor){
This.result= undefined;
This.state = “pendding”;
Executor(function(){},function(){})
}
Promise.prototype.then = function(resolved,rejected){
Return new Promise(function(){
If(this.state===”fulfilled”){
Resolved(this.value);
}else if(this.state === “rejected”){
Rejected(this.value)
}
}.bind(this));
}
12 ES6补充知识点
12.1 Symbol
12.1.1 基本使用
① Symbol的值是唯一的,用来解决命名冲突的问题
const p1 = Symbol();
const p2 = Symbol();
console.log(p1===p2); //false
② Symbol值不能与其他数据进行运算
// Symbol值不能与其他数据进行运算
console.log(s5+100);
注意:
i 通过for指定相同的参数可以制造一些相同
const s3 = Symbol.for("wang");
const s4 = Symbol.for("wang");
console.log(s3===s4);// true;
ii 引用外部的资源是,可能会覆盖之前写好的外部资源,可以采用symbol来解决
第一种方法:用for
引入Box
export default class {
constructor() {
this.a = 1;
this.b = 2;
}
}
<script type="module">
import Box from "./mo/Box.js"
const Box = new Box();
Box[Symbol.for("left")] = function() {
console.log("向左移动")
}
Box[Symbol.for("left")]();
</script>
第二种方法:从外部引入js
import Box from "./mo/Box.js";
import config from "./mo/configSymbol.js"
const Box = new Box();
const {left} = config;
Box[config.left] = function () {
console.log("向左");
}
Box[config.right] = function () {
console.log("向右");
}
Box[config.left]();
Box[config.right]();
引入的部分为
export default {
left:Symbol("left"),
right:Symbol("right"),
top:Symbol("top"),
down:Symbol("down")
}
iii Symbol类型唯一合理的用法是用变量存储 symbol的值,然后使用存储的值创建对象属性
12.1.2 内置值(了解)
除了定义自己使用的 Symbol 值以外,ES6 还提供了11个内置的Symbol值,指向语言内部使用的方法。
Symbol.hasInstance | 当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法 |
---|---|
Symbol.isConcatSpreadable | 对象的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 | 创建衍生对象时,会使用该属性 |
Symbol.replace :当对象当中的默写属性与其他变量有所关联时,可以使用该方法
const str = "abcdefg";
const myObject = {
a:1,
b:"前端的学习",
[Symbol.replace]:function(a,b) {//a=>abcfef,b=>z(12)
console.log(a,b,this);
// this.b.relace("学习","应用")
return 12
}
}
console.log(str.replace(myObject,"z"))/abcdef 12
12.2 迭代器
迭代器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。
a) Array
b) Arguments
c) Set
d) Map
e) String
f) TypedArray
g) NodeList
const arr = ["zhao","qian","sun","li"]
for (let item of arr) {
console.log(item) //zhao qian sun li
}
for(let [index,value] of arr.entries()){
console.log(index,value); //下标和值
}
for(let index of arr.keys()){// 下标
console.log(index);
}
for(let value of arr.values()){// 值
console.log(value);
}
- 工作原理
a) 创建一个指针对象,指向当前数据结构的起始位置
b) 第一次调用对象的next方法,指针自动指向数据结构的第一个成员
c) 接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员
d) 每调用next方法返回一个包含value和done属性的对象
// objSymbol是指针对象,结构:{value: "zhao", done: false}
const arr = ["zhao","qian","sun","li"]
const objSymbol = arr[Symbol.iterator]();
console.log(objSymbol.next());// 指针自动指向数据结构的第一个成员
console.log(objSymbol.next());// 向后推移
console.log(objSymbol.next());
console.log(objSymbol.next());
console.log(objSymbol.next());//value:undefined,done:true
注: 需要自定义遍历数据的时候,要想到迭代器。
const obj = {
userArr:["zhangsan","lisi","wangwu"],
Arr:["zhaoliu","qiangqi","shenba"],
[Symbol.iterator](){
let index = 0;
let arr = [...this.userArr,...this.Arr]
return {
next:()=> {
index++;
if (index <= this.userArr.length) {
return {
value:this.userArr[index-1],
done:false
}
} else {
return {
value:undefined,
done:true
}
}
}
}
}
}
for (let item of obj) {
console.log(item);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8Z5SpE0s-1620800127204)(C:\Users\王秀\AppData\Roaming\Typora\typora-user-images\image-20210507114414739.png)]可将next中的内容进行优化,并组合成多个数组
return {
value:arr[index],
done:index++<arr.length?false:true
}
12.3 Set
12.3.1 Set 基础
ES6 提供了新的数据结构 Set(集合)。它类似于数组,但成员的值都是唯一的,集合实现了iterator接口,所以可以使用『扩展运算符』和『for…of…』进行遍历,集合的属性和方法:
-
size 返回集合的元素个数
-
delete 删除元素,返回boolean 值
-
has 检测集合中是否包含某个元素,返回boolean值
-
clear 清空集合,返回undefined
const s1 = new Set([1,2,3,4,5,6,7,8,4,3]);
s1.add(12);// 添加
s1.add(8);// 添加注意,如果你添加的数据是重复的,那么不会添加成功
s1.delete(1);// 删除
console.log(s1.has(2));//true 返回一个布尔值,true:如果包含 指定的值,false:不包含指定的值
console.log(s1.has(21));//false 返回一个布尔值,true:如果包含 指定的值,false:不包含指定的值
console.log("数量",s1.size);// 8
s1.clear();
console.log("数量",s1.size);// 0
for(let item of s1){
console.log(item);
}
12.3.2 Set 应用
① 数组去重
const arr = ["zhangsan","lisi","wangwu","wangwu"];
console.log([...new Set(arr)]);
// zhangsan lisi wangwu ...可以将Set集合数据转换为数组
② 交集
// 交集
const arr = [1,2,3,4,5,6,3,2];
const arr2 = [4,5,6,7,8,9,5,7];
// 未优化
const newArr = [...new Set(arr)];
const newArr2 = newArr.filter(function (item) {
return new Set(arr2).has(item);// true false
})
console.log(newArr2); //4 5 6
// 优化
const newArr2 = [...new Set(arr)].filter(item=>new Set(arr2).has(item))
console.log(newArr2);
③ 并集
const arr = [1,2,3,4,5,6,3,2];
const arr2 = [4,5,6,7,8,9,5,7];
console.log([...new Set([...arr,...arr2])]);
// 1 2 3 4 5 6 7 8 9
④ 差集
const arr = [1,2,3,4,5,6,3,2];
const arr2 = [4,5,6,7,8,9,5,7];
// arr 与 arr2的差集
const newArr2 = [...new Set(arr)].filter(item=>!(new Set(arr2).has(item))) //1 2 3
// arr2与arr的差集
const newArr2 = [...new Set(arr2)].filter(item=>!(new Set(arr).has(item))) //7 8 9
console.log(newArr2);
12.4 Map
作为缓存处理。前端,后端(服务器)
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合。但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map也实现了iterator接口,所以可以使用『扩展运算符』和『for…of…』进行遍历。Map的属性和方法:
-
size 返回Map的元素个数
-
get 返回键名对象的键值
-
has 检测Map中是否包含某个元素,返回boolean值
-
clear 清空集合,返回undefined
// Map与对象的不同:1、Map的属性名(健名)可以设置成任意类型。
// 可以通过size获得元素个数
const obj = {a:1}
const m1 = new Map();
m1.set("one",1);
m1.set(obj,2);
m1.set(true,3);
m1.set(function(){},4);
console.log(m1,m1.get(obj));
console.log(m1.size) //4
m1.delete(obj);
console.log(m1.has(obj));//false
// m1.clear();
// console.log("结束",m1); //0
for(let [key,value] of m1){
console.log(key,value); //下标和值
}
for(let key of m1.keys()){
console.log(key) //下标
}
for(let value of m1.values()){
console.log(value) //值
}
12.5 数值扩展
12.5.1 二进制和八进制
0b 二进制
0o 八进制
0x十六进制
12.5.2 Number.isFinite()与Number.isNaN()
Number.isFinite() 用来检查一个数值是否为有限的,有限数:能够通过自然数表示的数字
Number.isNaN() 用来检查一个值是否为NaN
console.log(Number.isFinite(10));// true
console.log(Number.isFinite(10/0));// false
console.log(Number.isNaN(NaN));// true
console.log(Number.isNaN(1+undefined));// true
console.log(Number.isNaN(100));// false
console.log(NaN === NaN);// false
console.log(Object.is(NaN,NaN));// true
12.5.3 Number.parseInt()与Number.parseFloat()
ES6 将全局方法parseInt和parseFloat,移植到Number对象上面,使用不变。
console.log(parseInt(10.3));
console.log(Number.parseInt(10.3));
console.log(parseInt === Number.parseInt);// true
12.5.4 Math.trunc
console.log(Math.trunc(3.1415926)) //3
12.5.5 Number.isInteger
Number.isInteger() 用来判断一个数值是否为整数
console.log(Number.isInteger(10)); //ture
console.log(Number.isInteger(10.1)); //false
console.log(Number.isInteger("10")); //false
console.log(Number.isInteger(true)); //false
12.6 对象扩展
ES6新增了一些Object对象的方法
- Object.is 比较两个值是否严格相等,与『===』行为基本一致(+0 与 NaN)
console.log(Object.is(NaN,NaN));// true
console.log(Object.is(1,2));// false
-
Object.assign 对象的合并,将源对象的所有可枚举属性,复制到目标对象
-
proto、setPrototypeOf、 getPrototypeOf可以直接设置对象的原型
const obj = {a:1,b:2};
const obj2 = {c:3};
obj.__proto__ = obj2;
console.log(obj.__proto__ === obj2); //true
// 第一个参数是要更改哪个对象的原型,第二个参数是指定要将原型更改成什么。
Object.setPrototypeOf(obj,obj2);
console.log(obj); //{a:1,b:2}
// 获得obj对象的原型
console.log(Object.getPrototypeOf(obj)); //{c:3}
除
console.log(s1.has(2));//true 返回一个布尔值,true:如果包含 指定的值,false:不包含指定的值
console.log(s1.has(21));//false 返回一个布尔值,true:如果包含 指定的值,false:不包含指定的值
console.log(“数量”,s1.size);// 8
s1.clear();
console.log(“数量”,s1.size);// 0
for(let item of s1){
console.log(item);
}
#### 12.3.2 Set 应用
① 数组去重
```js
const arr = ["zhangsan","lisi","wangwu","wangwu"];
console.log([...new Set(arr)]);
// zhangsan lisi wangwu ...可以将Set集合数据转换为数组
② 交集
// 交集
const arr = [1,2,3,4,5,6,3,2];
const arr2 = [4,5,6,7,8,9,5,7];
// 未优化
const newArr = [...new Set(arr)];
const newArr2 = newArr.filter(function (item) {
return new Set(arr2).has(item);// true false
})
console.log(newArr2); //4 5 6
// 优化
const newArr2 = [...new Set(arr)].filter(item=>new Set(arr2).has(item))
console.log(newArr2);
③ 并集
const arr = [1,2,3,4,5,6,3,2];
const arr2 = [4,5,6,7,8,9,5,7];
console.log([...new Set([...arr,...arr2])]);
// 1 2 3 4 5 6 7 8 9
④ 差集
const arr = [1,2,3,4,5,6,3,2];
const arr2 = [4,5,6,7,8,9,5,7];
// arr 与 arr2的差集
const newArr2 = [...new Set(arr)].filter(item=>!(new Set(arr2).has(item))) //1 2 3
// arr2与arr的差集
const newArr2 = [...new Set(arr2)].filter(item=>!(new Set(arr).has(item))) //7 8 9
console.log(newArr2);
12.4 Map
作为缓存处理。前端,后端(服务器)
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合。但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map也实现了iterator接口,所以可以使用『扩展运算符』和『for…of…』进行遍历。Map的属性和方法:
-
size 返回Map的元素个数
-
get 返回键名对象的键值
-
has 检测Map中是否包含某个元素,返回boolean值
-
clear 清空集合,返回undefined
// Map与对象的不同:1、Map的属性名(健名)可以设置成任意类型。
// 可以通过size获得元素个数
const obj = {a:1}
const m1 = new Map();
m1.set("one",1);
m1.set(obj,2);
m1.set(true,3);
m1.set(function(){},4);
console.log(m1,m1.get(obj));
console.log(m1.size) //4
m1.delete(obj);
console.log(m1.has(obj));//false
// m1.clear();
// console.log("结束",m1); //0
for(let [key,value] of m1){
console.log(key,value); //下标和值
}
for(let key of m1.keys()){
console.log(key) //下标
}
for(let value of m1.values()){
console.log(value) //值
}
12.5 数值扩展
12.5.1 二进制和八进制
0b 二进制
0o 八进制
0x十六进制
12.5.2 Number.isFinite()与Number.isNaN()
Number.isFinite() 用来检查一个数值是否为有限的,有限数:能够通过自然数表示的数字
Number.isNaN() 用来检查一个值是否为NaN
console.log(Number.isFinite(10));// true
console.log(Number.isFinite(10/0));// false
console.log(Number.isNaN(NaN));// true
console.log(Number.isNaN(1+undefined));// true
console.log(Number.isNaN(100));// false
console.log(NaN === NaN);// false
console.log(Object.is(NaN,NaN));// true
12.5.3 Number.parseInt()与Number.parseFloat()
ES6 将全局方法parseInt和parseFloat,移植到Number对象上面,使用不变。
console.log(parseInt(10.3));
console.log(Number.parseInt(10.3));
console.log(parseInt === Number.parseInt);// true
12.5.4 Math.trunc
console.log(Math.trunc(3.1415926)) //3
12.5.5 Number.isInteger
Number.isInteger() 用来判断一个数值是否为整数
console.log(Number.isInteger(10)); //ture
console.log(Number.isInteger(10.1)); //false
console.log(Number.isInteger("10")); //false
console.log(Number.isInteger(true)); //false
12.6 对象扩展
ES6新增了一些Object对象的方法
- Object.is 比较两个值是否严格相等,与『===』行为基本一致(+0 与 NaN)
console.log(Object.is(NaN,NaN));// true
console.log(Object.is(1,2));// false
-
Object.assign 对象的合并,将源对象的所有可枚举属性,复制到目标对象
-
proto、setPrototypeOf、 getPrototypeOf可以直接设置对象的原型
const obj = {a:1,b:2};
const obj2 = {c:3};
obj.__proto__ = obj2;
console.log(obj.__proto__ === obj2); //true
// 第一个参数是要更改哪个对象的原型,第二个参数是指定要将原型更改成什么。
Object.setPrototypeOf(obj,obj2);
console.log(obj); //{a:1,b:2}
// 获得obj对象的原型
console.log(Object.getPrototypeOf(obj)); //{c:3}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。