8857 字
44 分钟
ES6++ 笔记
ES6 及以上版本 JavaScript 新特性详细笔记,涵盖 let/const、块级作用域、变量提升、解构赋值、箭头函数、Promise、模块化等核心内容,适合前端开发者学习与查阅。
2026-02-03
0 次
0 人
一,ES6++
什么是ECMA
// 中文名称是欧洲计算机制造商协会,这个组织的目的是评估,开发和认可电信和计算机标准。1994年后该组织改名为ECMA国际什么是ECMAscript
// ECMAscript是由ECMA 国际通过ECMA-262标准化的脚本程序设计语言1,ES6
1.1 let变量声明以及声明特性
//变量不能重复声明let star = 'zll';let star = 'wxl';//此时会报错
//块级作用域有效 全局、函数、ecal{ //if for else while let str='zzl';}console.log(str);//此时会报错,因为拿不到
// 不存在变量提升console.log(name);//会报错,不能使用name这个变量let name='zzl';
//不影响作用域链{ let name='zzl';//虽然是块级作用域,但是不影响作用域链的效果 function fn(){ console.log(name); } fn();//可以拿到}1.1.1 let const和var的区别?
// var 存在变量提升(在声明语句代码之前可以使用,不会报错) let const 存在暂时性死区(声明之后在声明语句后面代码才可以用)
console.log(a) console..log(a) 报错 console.log(a) 报错var a = 10 let a = 10 const a = 10
// vat 声明的变量可以重复声明 let const声明的变量不能重复声明var a = 10 let a = 10 const a = 10var a = 20 let a = 20 报错 const a = 14 报错// var 没有块级作用域 let const 有块级作用域(例如:在while for 循环中)for(var i = 0;i<10;i++){} for(let i = 0;i<10;i++){}i泄漏为全局变量,存在于全局变量中 i有块级作用域
// var let 是声明变量,变量值可以改变 cosnt 声明常量,值不能改(准确来说,内存地址不能变),而且声明时必须赋值1.2 const 声明常量以及特点
1,const声明常量2,一定要赋初始值3,常量的值不能再修改4,也是块级作用域,5,声明常量
const CL='zzl';// 对于数组和对象的元素修改,不算2中的常量修改,不会报错const SZ=['zzl','wxl'];SZ.push('ll');//此时不会报错,因为只是修改数据,并没有修改地址1.3 变量的解构赋值
//数组解构赋值 const nameS=['zzl','wxl','ll']; let [name1,name2,name3]=nameS; console.log(name1);//输出zzl
//对象的结构赋值 const data={ name:'zzl', age:'22', active:function(){ console.log('我会飞'); } } let {name,age,active}=data; console.log(name); // 输出zzl active(); // 调用方法,输出我会飞
let {active} = data;active();1.4 模板字符串
let str=`字符串`
// 它可以支持换行// 变量拼接
let name='zzl';let data=`${name}是个人` // 'zzl是个人'1.5 对象简化写法
// es6允许在大括号里面直接写入变量名和函数名,作为对象的属性和方法
let name='zzl';let fun=function(){ console.log('我是人');}const data={ name, //可以直接写变量名,不用再写值了 fun}console.log(data); // 结果输出 name:'zzl',fun()
const data={ // 旧的方法定义 // act:function(){ // console.log('我会动'); // } //新的方法定义 act(){ console.log('我会动'); }}1.6 箭头函数以及声明特点
// 声明一个函数let fn=function(a,b){}// 箭头函数声明一个函数let fn=(a,b)=>{}fn(1,2);1.6.1 特点
// 箭头函数的额this是静态的,this始终指向函数声明时所在作用域下的this的值function getname(){ //name是zzl,直接调用的话this值指向window} console.log(this.name);//zzllet getname2=()=>{ //而这个箭头函数是在全局作用域下声明的,所以this也是指向window console.log(this.name);//zzl}
window.name='zzl';const data={ name:'wxl'}
//直接调用getname();//name是zzl,直接调用的话this值指向windowgetname2();//而箭头函数是在全局作用域下声明的,所以this也是指向window
// call方法调用getname.call(data);//此时普通函数this的值已经变为data了。getname2.call(data);//输出wxl,因为它的this依然指向函数getname2声明时所在作用域下的this的值window.name='zzl';
////////////////////////////////////////////////////////////////////////////////
// 不能作为构造实例化对象let Person=(name,age)=>{ this.name=name; this.age=age;}let me=new Person('zzl','22');//此时会报错,箭头函数不能作为实例化对象。
////////////////////////////////////////////////////////////////////////////////
//不能使用arguments变量// arguments:用来保存实参的let fn=()=>{ console.log(arguments);}fn(1,2,3);//会报错,不用用来保存实参1.6.2 简写箭头函数
// 省略小括号let add=(n)=>{ return n+n;}console.log(add(9));
//简写为下面----------
//当形参有且只有一个的时候,可以省略小括号let add=n=>{ return n+n;}console.log(add(9));
////////////////////////////////////////////////////////////////////////////////
//省略花括号//当代码体只有一条语句的时候
let sum=(n)=>{ return n*n;};console.log(sum(9));
//简写为下面----------
//当代码体只有一条语句的时候,此时return必须省略,而且语句的执行结果就是函数的返回值let sum=(n)=>n*n;console.log(sum(9));
//小括号和花括号同时省略----------let sum=n=>n*n;console.log(sum(9));1.6.3 箭头函数注意事项
// 箭头函数适合与this无关的回调,比如定时器,数组的方法回调。// 箭头函数不适合与this有关的回调,比如DOM元素的事件回调、对象的方法。
btn.addEventListener("click",function(){ //此时普通函数的this指向事件缘 //如果使用箭头函数,事件源将变成外部作用域的this值,即这个函数所在的作用域})1.6.4 箭头函数中的this
// 箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this
function Person() { this.age = 0;
setInterval(function growUp() { this.age++; // 定时器中使用普通函数,this永远指向window,因为是window调用定时器 所以this.age 值得是Nan }, 1000);}
var p = new Person();// '------------------------------------'function Person() { this.age = 0 setInterval(()=> { // 箭头函数没有自己this,只能往上一层去找 console.log(this) // Person对象 this.age++; console.log(this.age) }, 1000);}var p = new Person();1.6.5 笔试题
var myObject = { foo: "bar", func: function () { var self = this; // self = myObject console.log(this.foo); // 'bar' console.log(self.foo); // 'bar' (function () { console.log(this.foo); // undefined // 立即执行函数里面this永远指向window console.log(self.foo); // 'bar' }()); }};myObject.func();
// ----------------------------------------------------------------------------
function plugin () { this.a = 1 this.b = { x: 1, confirm: function(title, callback) { this.title = title console.log(this, '2') callback() } } console.log(this, '1')}var obj = new plugin()obj.b.confirm('弹窗', () => { console.log(this, '3')})1.6.6 箭头函数与普通函数的区别?
1,this 的指向问题 // 箭头函数没有自己的作用域,所以没有自己的this,箭头函数中的this指向箭头函数当前函数声明时的作用域链的上一层 // 普通函数有自己的作用域2,call() 和apply() // 箭头函数里this的指向不受call,apply的指向,只跟定义位置作用域链的上一层有关,而普通函数可以3,箭头函数不能当构造函数,而且没有Prototype属性,而普通函数可以4,箭头函数没有arguments对象,普通函数有arguments对象,箭头函数中的参数可以用...参数名称来获取剩余参数5,箭头函数不能作为generator函数,普通函数可以1.6.7 箭头函数的this的指向?
// 箭头函数里的this指向沿着其作用域链往上找代码的执行结果是什么?
1.7 函数参数初始值
function add(a,b,c=10){}
//函数参数初始值与结构赋值结合使用function data({name,age='22'}){ console.log(name); console.log(age);}
data({ name='zzl', // age='23'})1.7.1 rest参数
// 用于获取函数的实参,可以代替arguments。
function data(){ console.log(arguments); // 输出的是一个对象}data('zzl','wxl');
// rest参数function data(...args){ console.log(args); //输出的是一个数组,可以使用filter some...}data('zzl','wxl');
// --------------------------------------------------------------
// rest参数必须放在参数最后。
function data(a,b,...args){ console.log(a);//1 console.log(b);//2 console.log(args);// [3,4,5]}data(1,2,3,4,5)
// --------------------------------------------------------------------------------------
const obj1={ q:'zzl'}const obj2={ s:'wxl'}const data={...obj1,...obj2}; //相当于合并了两个对象console.log(data); // {q:'zzl',s:'wxl'}1.8 扩展运算符
// 它能将数组转换为逗号分隔的参数序列
const names=['zzl','wxl','ll'];function data(){ console.log(arguments);}
//不用扩展运算符data(names); // 只输出一个结果,是一个数组// 用扩展运算符data(...names); // 输出3个结果,等价于:data('zzl','wxl','ll'),即参数序列
// -------------------------------------------------------- // 如果数组里面有引用类型的话,扩展呢运算符也只是浅拷贝。 // 可以用来数组的合并 // 数组的克隆 // 伪数组转为真正的数组 // 数组的合并
const a=['zzl'];const b=['wxl'];const c=[...a,...b]; // c=['zzl','wxl']
//将伪数组转为真正的数组const divs=document.querySelectorAll('div'); // 得到伪数组const divdata=[...divs]; // 转为真正的数组1.8 symbol
// 它一种新的数据类型,表示独一无二的值,类似于字符串的数据类型。
// 它的值是唯一的,用来解决命名冲突的问题。// 它的值不能于其他数据进行运算。// 它定义的对象属性不能使用for…in 循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名。
// 创建symbollet s = Symbol();
// 下面的两个symbol对象时不一样的let s2 = Symbol('aini')let s3 = Symbol('sini')
// symbol.for创建let s4 = Symbol.for('aini')let s5 = Symbol.for('aini')
let game={ //假如有很多代码很多变量名}
//声明一个对象let data={ //Symbol保证了up和down的属性名是独一无二的, // 所以添加进去也不怕也不怕有属性名冲突
up:Symbol(), //up属性的数据类型为Symbol
down:Symbol()};
//第一种添加方式//把这个Symbol添加到game方法中game[data.up]=function(){ console.log('我会飞'); //安全的向这个对象中添加了两个方法}game[data.down]=function(){ console.log('我会爬');}console.log(game);
//////////////////////////////////////
//第二种添加方式 let play={ name='run', [Symbol('say')]:function(){ console.log('我会说话'); }, [Symbol('sleep')]:function(){ console.log('我会睡觉'); } } console.log(paly);
//补充代码 const title1 = Symbol("title") const title2 = Symbol("title") const info={ [title1]:'zzl', [title2]:'wxl' }Symbol有很多内置方法1.8.1 Symbol的数据类型及特性?
// 数据类型的声明let sym = Symbol()// 作用:保证每一个对象的名字都是独一无二的
// 可以接受字符串作为参数,作位对Symbol实例的描述let sym = Symbol('aini')// symbol 的参数是一个对象,可以调用该对象的toString 方法将其转换为字符串,然后生成一个symbol值// symbol函数的参数只是对当前Symbol 值的描述,因此相同参数的Symbol函数返回的值是不相等的let s1 = SYmbol('s1') s2 = Symbol('s1') cosnole.log(s1 === s2) // false
// symbol值不能与其他类型的值进行运算,否则会报错
//symbol 值可以显示转换为字符串和Boolean 但是不能转换为Number
// Object.getOwnPropertySymbol 方法可以获取指定对象的所有Symbol属性名
// symbol声明的属性是没办法通过Object.key() 拿到,所以可以实现私有属性1.8.2 Symbol.iterator的作用?
// Symbol.iterator 为每一个对象定义了默认的迭代器。该迭代器可以被for ... of 循环使用1.9 迭代器
任何数据结构只要部署了Iterator接口,就可以使用for…of 来遍历.
1.9.1 具备iterator接口的数据类型
// Array// Argunments// Set// Map// String// TypedArray// NodeList
// 这个接口就是对象里面的一个属性,属性的名字叫Symbol.iterator,也可以自己对结构进行布置iterator接口。
// -------------------------------------------------------------------------------------------
for( let i in data){ i是键名}for( let i of data){ i是键值}const arr=['zzl','wxl'];console.log(arr);//arr里面就有Symbol.iterator这个属性。1.9.2 工作原理
// 先创建一个指针对象,指向当前数据结构的起始位置// 第一次调用对象的next方法,指针自动指向数据结构的第一个成员// 接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员// 每次调用next方法就会返回一个包含value和done属性(是否完成)的对象
let xiyou = ['唐曾','孙悟空','猪八戒','沙僧']
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}1.9.3 手写迭代器
//声明一个对象const data={ name:'zzl', lis:[ 'wxl', 'll', 'hll' ],
//自己给某些结构加上iterator接口 [Symbol.iterator](){ //索引变量 let index=0; let _this=this; return { //返回一个指针对象,即创建一个指针对象 next:function(){ //创建对象的next方法 // 返回一个包含value和done属性(是否完成)的对象 if(index < _this.lis.length){ const result = { value:_this.lis[index], done:false}; index++; return result; }else{ return {value: undefined, done:true}; } } }; }}
//自定义遍历这个对象for(let v of data){ console.log(v);}console.log('--------------------')console.log(data);1.9.4 iterator接口的作用?
// Iterator 是一个接口,用于处理所有不同的数据结构遍历器。只要数据结构定义了Iterator接口时可以用for of 来循环遍历
// for of 循环的执行过程 var arr = ['blue','green','yellow','balck','orange'] let it = iterator(arr) console.log(it.next()) console.log(it.next()) console.log(it.next()) console.log(it.next())
function iterator(arr){ let nextIndex = 0 return { next: function() { return nextIndex < arr.length ? { value: arr[nextIndex ++] ,done: true } : { value : undefined,done : false } } } }
// Iterator 接口部署在数据结构的Symbol.iteratro属性// 一个数据结构只要具有Symbol.Iterator属性,就可以认为是'可遍历的'// Symbol.Iterator 属性是一个函数,每次运行会返回一个生成器对象包含俩属性value和done
// 原生具备Iterator接口的数据结构// Array Map Set String 函数的arguments对象
// 伪数组转换为真正数组的方法1, Array.prototype.slice.call(arguments)2, Array.from(arguments)
//1.10 数组的常用方法
1.10.1 forEach()
// 第一个参数:数组元素;// 第二个参数:数组索引;// 第三个元素:数组对象本身
var numbers = [1,2,3,4,5,4,3,2,1];numbers.forEach(function(item,index,array){ console.log(item,index,array[index]);});
// -----------------------------------------------------------// 为每个元素加 1var data = [1,2,3,4];data.forEach(function(v,i,a){ a[i] = v + 1; //会改变原数组元素});console.log(data);
// 如果只关心数组元素的值,可以只使用一个参数,额外的参数将被忽略
// ------------------------------------------------------------------// 求和var data = [1,2,3,4];var sum = 0;data.forEach(function(value){ //第一个参数数组元素,相当于遍历多有元素; sum += value; //不会改变原数组元素;});console.log(sum);
// -------------------------------------------------------------------
// (1)forEach()方法无法在所有元素都被传递给调用的函数之前终止遍历,即没有像 for 循环中使用的 break 语句;// (2)如果要提前终止,必须把 forEach()方法放在 try 块中,并能抛出一个异常:function foreach(a,f,t){ try{ a.forEach(f, t); } catch(e){ if(e === foreach.break) return; else throw e; }}foreach.break = new Error("StopIteration");1.10.2 every() ------------ some()
//(1)最相似的是 every()和 some(),它们都是数组的逻辑判定:用于判定数组元素是否满足某个条件;//(2)对 every(),传入的函数必须对每一项都返回 true,这个方法才返回 true;//(3)而 some()是只要传入的函数对数组中的某一项返回 true,就会返回 true,否则返回 false;
var numbers = [1,2,3,4,5,4,3,2,1];var everyResult = numbers.every(function(item,index,array){ return (item > 2);});console.log(everyResult); // false
var someResult = numbers.some(function(item,index,array){ return (item > 2);});console.log(someResult); // true
// (4)一旦 every()和 some()确定该返回什么值时,它们就会停止遍历数组元素;// (5)(some()在判定函数第一次返回 true 后就返回 true,但如果判定函数一直返回 false,它将会遍历整个数组;every()恰好相反,它在判定函数第一次返回 false 后就返回 false,但如果判定函数一直返回 true,它将会遍历整个数组)。// (6)根据数学上的惯例,在空数组上调用时,every()返回 true,some()返回 false;1.10.3 filter()
//(1)返回的是数组元素是调用的数组的一个子集;//(2)回调函数是用来逻辑判定的,该函数返回 true 或 false;如果返回的是 true 或真值,则该函数处理的数组元素就被添加到返回的子集数组中;//(3)filter()方法会跳过稀疏数组中缺少的元素,它的返回数组总是密集的
var numbers = [1,2,3,4,5,4,3,2,1];var filterResult = numbers.filter(function(item,index,array){ return item>2;});console.log(filterResult);
var evenResult = numbers.filter(function(value,index){ return index % 2 == 0; // [1, 3, 5, 3, 1] index 是索引});console.log(evenResult);
// 压缩稀疏数组var sparse = [1,,,4];var dense = sparse.filter(function(){ return true; //过滤稀疏元素});console.log(dense);
// 压缩并删除 undefined 和 null 元素var sparse = [1,null,3,undefined,,6];var dense = sparse.filter(function(v){ return v !== undefined && v != null;});console.log(dense);
//(注:该方法返回的是符合条件的数组元素;)1.10.4 map()
//(1)将调用的数组的每个元素传递给回调函数,并将调用的结果组成一个新数组返回;//(2)其不会修改原始数组,如果是稀疏数组,返回的也是相同方式的稀疏数组,即具有相同的长度、相同的缺失元素;
var numbers = [1,2,3,4,5,4,3,2,1];var mapResult = numbers.map(function(item,index,array){ return item*2;});console.log(mapResult);
//-------------------------------------------------var a = [1,null,3,undefined,5];var b = a.map(function(v){ return v * v;});console.log(b);// --------------------------------------------
//不做任何处理var spare = [1,,4,null,undefined,NaN,6];var dense = spare.map(function(v,i,a){});console.log(dense);
// ----------------------------------------------------// 返回所有数组元素var spare = [1,,4,null,undefined,NaN,6];var dense = spare.map(function(v,i,a){ return v;});console.log(dense);
// -----------------------------------------------------------//能处理的元素进项处理,空元素返回空元素;var spare = [1,,4,null,undefined,NaN,6];var dense = spare.map(function(v,i,a){ return v*v; //进行数据类型转换,由于 null 和 undefined 不能进行数据类型转换});console.log(dense) //,所以都返回 NaN1.10.5 reduce() --------------------- reduceRight()
//(1)reduce()和 reduceRight();这两个方法都会迭代数组的所有项,然后构建一个最终返回的值;//(2)其中,reduce()方法从数组的第一项开始,逐个遍历到最后;而 reduceRight 则从数组的最后一项开始,向前遍历到第一项;//(3)这两个方法都接收两个参数:调用的函数 callbackfn 和作为归并基础的初始值initialValue(可选的);//(4)这个函数接收 4 个参数:前一个值、当前值、项的索引和数组对象;其返回的任何值都会作为第一个参数自动传给下一项;第一次迭代发生在数组的第二项上,因此第一个参数是数组的第一项,第二个参数就是数组的第二项
// 求和var values = [1,2,3,4];var sum = values.reduce(function(prev,cur,index,array){ return prev + cur;});console.log(sum); // 10 // 第一次遍历:prev=1 cur=2; // 第二次遍历:prev=3 cur=3; // 第三次遍历:prev=6 cur=4; // 第四次遍历:prev=10/* 说明:第一次执行回调函数,prev 与 cur 分别为数组的第 1 和第 2 项,即 prev 是 1,cur 是 2;第二次, prev 是第一次执行的结果,即 3(1+2 的结果),cur 是数组的第三项,即是 3,以此类推; reduce()参数函数的参数也可以只使用两个参数,如:*/
//-------------------------------------------------------------------------------// 求和var values = [1,2,3,4]; var sum = values.reduce(function(prev,cur){return prev + cur;});console.log(sum); // 10
// ----------------------------------------------------------------------------------// 求积var product = values.reduce(function(prev,cur){ return prev * cur;});console.log(product); // 24
//------------------------------------------------------------------------------// 求最大值var max = values.reduce(function(prev,cur){ return prev > cur ? prev : cur;});console.log(max); // 4// reduce()方法的第二个参数 initialValue 是回调函数的第一个参数 previousValue 的初始值;如:// 把以上所有示例添加第二个参数,如:var values = [1,2,3,4];var sum = values.reduce(function(prev,cur){return prev + cur;},2); // 12
/*意思就是 pre 不是第一项,而是给 pre 赋一个值,cur 从第一项开始迭代说明,第一次调用时,prev 为 initialValue 值,即为 2,cur 为数组第一项,即为 1;第二次调用,prev 为 3,cur 为 2,以此类推;reduceRight()的作用类似,只不过方向相反,即按照数组索引从高到低(从右到左)处理数组;如:*/var values = [1,2,3,4,5];var sum = values.reduceRight(function(prev,cur,index,array){ return prev + cur; //21},6);//pre 的初始值为 6;console.log(sum); // 15
// ---------------------------------------------------------------------// 求 2^(3^4),乘方操作的优先顺序是从右到左var a = [2,3,4];var big = a.reduceRight(function(accu, value){ return Math.pow(value, accu);});console.log(big);
//在空数组上,不带初始值参数的 reduce()将导致类型异常;如果数组只有一个元素并且没有指定初始值,或者一个空数组并且指定了一个初始值,该方法只是简单的返回那个值而不会调用回调函数;
var a = [];// var b1 = a.reduce(function(x,y){return x + y}); // no initial value 会报错// var b2 = a.reduce(function(x,y){return x + x}); / /也会报错var b3 = a.reduce(function(x,y){return x + y},3); //只返回初始值console.log(b3); // 3
// (1)使用 reduce()还是 reduceRight(),主要取决于要从哪头开始遍历数组;除此之外,它们完全相同。//(2)reduce()和 reduceRight()是典型的函数式编程;有些地方,会把其回调函数称为化简函数,这个化简函数的作用就是用某种方法把两个值组合或化简为一个值,并返回化简后的值;如以上的示例,化简函数通过各种方法,组合了两个值1.11 生成器
// 生成器就是一个特殊的函数,是异步编程新的解决方案。分别通过以下代码可知运行逻辑。
function * fun(){ console.log('zzl');}let a=fun();// console.log(a);//输出一个迭代器对象(有next方法)a.next(); // 输出zzl
// --------------------------------------------function * fun(){ console.log('zzl'); console.log('wxl');}let a=fun();console.log(a);//输出一个迭代器对象(有next方法)a.next();//输出zzl wxl 即全部输出
// -------------------------------------------------------------function * fun(){ console.log('zzl'); yield '我要暂停' console.log('wxl');}let a=fun();console.log(a);//输出一个迭代器对象(有next方法)a.next();//输出zzl 我要暂停
// -----------------------------------------------------------------------------function * fun(){ console.log('zzl'); yield '我要暂停' console.log('wxl');}let a=fun();// console.log(a);//输出一个迭代器对象(有next方法)a.next();//输出zzla.next();//输出wxlfunction * fun(){ // console.log('zzl'); yield '我要暂停' // console.log('wxl');}let a=fun();console.log(a);//输出一个迭代器对象(有next方法)for(i of fun()){ console.log(i);//我要暂停}
// -----------------------------------------------------function * fun(){ // console.log('zzl'); yield '我要暂停' // console.log('wxl');}let a=fun();console.log(a.next()); //输出 {value: '我要暂停', done: false}1.11.1 生成器函数的参数传递
function * fun(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 a=fun('aaa');console.log(a.next());//第一次调用next//next方法可以传入实参//第二次调用next的实参将作为第一个yield的整体返回结果console.log(a.next('bbb')) // 输出{value: 222, done: false}console.log(a.next('ccc')) // 输出{value: 333, done: false}console.log(a.next('ddd')) // 输出{value: undefined, done: true}1.11.2 生成器函数实例
// 实例1:在控制台每隔一秒分别打印111,222,333// 回调地狱
setTimeout(() => { console.log(111); setTimeout(() => { console.log(222); setTimeout(() => { console.log(333); }, 3000); }, 2000); }, 1000);
// 解决方法 function one(){ setTimeout(()=>{ console.log(111); a.next(); //定时器运行完调用下一个,实现了异步编程 },1000) } function two(){ setTimeout(()=>{ console.log(222); a.next(); },2000) } function three(){ setTimeout(()=>{ console.log(333); a.next(); },3000) }
function *fun(){ yield one(); yield two(); yield three(); }
//调用生成器函数let a=fun();a.next();结果演示
// -------------------------------------------------------------------------------------------------// 实例2 模拟获取用户数据,订单数据,商品数据 function one(){ setTimeout(()=>{ let data='用户数据'; a.next(data);//第二次调用next的实参将作为第一个yield的整体返回结果 },1000) } function two(){ setTimeout(()=>{ let data='订单数据'; a.next(data); },1000) } function three(){ setTimeout(()=>{ let data='商品数据'; a.next(data); },1000) } function *fun(){ let users= yield one(); console.log(users); //用户数据 作为第一个yield的整体返回结果 let orders= yield two(); console.log(orders); // 订单数据 let goods= yield three(); console.log(goods); // 商品数据 } //调用生成器函数 let a=fun(); a.next();1.12 Promise
// 它是es6中异步编程的新的解决方案。相当于一个构造函数。
function fun(){ return new Promise((resolve,reject)=>{ //如果成功就调用resolve let data='数据库中的数据'; resolve(data); //promise状态变为成功
//如果失败就调用reject let err='数据库读取失败'; reject(err);//promise状态变为失败 })}
var promise = fun()promise.then( //调用then方法 function(success){ // promise状态变为成功后then会调用第一个回调函数 console.log(success); },function(err){ // promise状态变为失败后then会调用第二个回调函数 console.log(err); })1.12.1 promise封装读取文件
cosnt fs = require('fs')
fs.readFile('./aini.txt',(err,data) => { // 如果失败,则抛出异常 if(err) throw err; // 如果没哟出错,则输出内容 console.log(data.toString())})
// 使用Promise封装cosnt p = new Promise(function(resolve,reject){ fs.readFile('./aini.txt',(err,data) => { // 如果失败,则抛出异常 if(err) reject(err) // 如果没哟出错,则输出内容 resolve(data) })})
p.then(function(value){ console.log(value.toString())
},function(resson){ console.log('读取失败!!!')
})1.12.2 Promise 封装Ajax请求
const p = new Promise(function(resolve,reject){
cosnt xhr = new XMLHttpRequest()
xhr.open('GET','https://api.apiopen.top/getJoke')
xhr.send()
xhr.onreadystatechange = function(){ if (xhr.randyState == 4){ // 判断状态码 if (xhr.status >= 200 && xhr.status <300){ // 表示成功 resolve(xhr.response) }else{ reject(xhr.status) } } }})
p.then(function(value){ console.log(value.toString())
},function(resson){ console.log(reason)})1.12.3 Promise.property.then()
const p = new Promise((resolve,reject){ setTimeout(() => { resolve('用户数据') },1000)})
// 调用,then方法的结果也是promise对象,结果由回调函数的结果决定// 1,如果回调函数返回的结果是非promise类型的数据,状态为成功,返回值为对象的成功值,如果不写return,默认返回undefined,也是成功的promise const p1 = p.then(value => { console.log(value) return '11232' // 成功的Promise }.err => { console.warn(reason) })
// 2,如果返回的是promise对象,则这个promise对象的返回结果决定,then方法返回的结果 const p1 = p.then(value => { console.log(value) return new Promise(funtion(resolve,reject){ resolve('ok') // 成功,则then方法返回结果是成功 reject('失败了') // 失败,则then方法返回的结果是失败的 ) }.err => { console.warn(reason) })
// 如果抛出错误,then 也是个失败的结果 const p1 = p.then(value => { console.log(value) throw new Error('出错啦') // then的状态为rejected }.err => { console.warn(reason) })
// 链式调用p.then(value=> { return new Promise(function(resoleve,reject){ resolve() })},reason => {
}).then(value=> { return new Promise(function(resoleve,reject){ resolve() })},reason => {
}).then(value=> { return new Promise(function(resoleve,reject){ resolve() })},reason => {
})1.12.4 Promsie 多个文件内容读取
// 回调地狱cosnt fs = require('fs')
fs.readFile('./aini.txt',(err,data1) => { fs.readFile('./norah.txt',(err,data2) => { fs.readFile('./dilnur.txt',(err,data3) => { let reault = data1 + data2 + data3 }) })})
// 使用promise实现const p = new Promise(function(resolve,reject){ fs.readFile('./aini/txt',(err,data) => { resolve(data) })})
p.then(value => { return new Promise((resolve,reject) => { fs.readFile('./norah.txt',(err,data) => { resolve([value,data]) }) })}).then(value => { return new Promise((resolve,reject) => { fs.readFile('./dilnur.txt',(err,data) => { value.push(data) resolve(data) }) })}).then(value => { console.log(value.join('\r\n'))})1.12.5 Promise对象的catch()方法
const p = new Promise((reslove,reject)=>{ setTimeout(() => { reject('出错了') },1000)})
// 第一种 p.then(function(value){ // 成功的结果 },function(reason){ // 失败的结果 })
// 第二种 p.then(function(value){ // 成功的结果 }).catch(function(reason){ //失败的结果 })
// 第三种 p.then(function(value){ // 成功的结果 })
p.catch(function(reason){ //失败的结果 })1.13 Set
Set的数据类型及特性?
// set 是ES6新增的复杂数据类型,类似于数组,但是值没有重复分// 新建 const s = new Set()// 可以接受数组或字符串当参数 const s = new Set([1,2,3,4,2,3]) const s1 = new Set('abcdeftg')// set 可以转化成数组,通过...扩展运算符或者Array.from()方法 let arr = [...s] // 等同于 Array.from(s) let arr1 = [...s1] // 等同于 Array.from(s1)// set 用于数组去重
// set 里面的判断机制是 === 运算符 (NaN 例外)let set = new Set()set.add(5)set.add('5') // 5 '5' 是两个元素
// set 实例的属性和方法1,size() 返回长度2, has() 判断是否包含某个元素2,forEach 方法 let set = new Set([1,3,2,3,54,3,2]) console.log(set) set.forEach((item,index) => console.log(item + '-----------' + index)) // item 和 key 值一样// 并集let arr = [1,2,3,4]let arr1 = [2,3,4,5,6]let a = new Set(arr)let b = new Set(arr1)let set = new Set([...a,...b])
// 交集let arr = [1,2,3,4]let arr1 = [2,3,4,5,6]let a = new Set(arr)let b = new Set(arr1)
let intersect = new Set([..a].filter(x => b.has(a)))
// 差集let arr = [1,2,3,4]let arr1 = [2,3,4,5,6]let a = new Set(arr)let b = new Set(arr1)
let chaji = new Set([..a].filter(x => !b.has(a)))1.14 Map
Map的数据类型及特性?
// Map 是ES6新增的复杂数据类型,类似于对象,也是键值对集合,但是键的范围不限于字符串,各种类型的值都可以当做键// Map 是值与值的对应,是更完美的键值对结构的提现
// 新建Map数据的方法1, const m = new Map()
// Map 的方法1, set() m.set({x: 1}, 22)2, get m.get({x: 1})3, has() 判断某个键是否存在 m.has({x:1})4, delete() 删除某个键值对
// 判断是不是Map类型的数据结构let map = new Map()1, map instanceof Map // true 通过原型链查找2, Object.prototype.toString.call(map) // [object, Map]
// 为什么 Object.prototype.toString.call() 用的是原型链顶端的toString方法呢?// 因为数组和对象对toString方法都进行了改写
// 可以接受数组作为参数// 注意的是:数组成员是一个个表示键值对的数组 let map = new Map( [ ['name','aini'], ['age',22], [{name:'norah'},'这是一个对象'] ] ) // 一个坑 map.get({name:'norah'}) // undefined 是因为 {} 与 {} 不是同一个对象
// 解决办法:let obj = {name:'norah'}let map = new Map( [ ['name','aini'], ['age',22], [obj,'这是一个对象'] ] )map.get(obj) // 这样才能获取正确的key值
// map参数的扩展// 任何具有 Iterator 接口,且每个成员都是一个双元素的数组的数据结构,都可以当做map函数的参数1.15 Class类
// 以手机类为例function Phone(brand,price){ this.brand = brand this.price = price}// 静态成员//相当于 python里的staticmethodPhone.name = '手机'Phone.change = function(){ console.log('我可以改变世界')}
// 相当于Python里的classmethodPhone.ptototype.size = "5.5英寸"Phone.prototy.call = function() { console.log('我可以打电话')}
ler Huawei = new Phone('华为',5999)
// ES6语法class Phone{ // 构造函数方法 constructor(brand,price){ this.brand = brand this.call = call }
// 语法必须使用这个方法,不能用ES5老语法 call() { console.log('我可以打电话') }}
ler Huawei = new Phone('华为',5999)1.15.1 构造函数继承
function Phone(brand,price){ this.brand = brand this.price = price}
Phone.prototype.call = function(){ console.log('我可以打电话')}
//智能手机function SmartPhone(brand,price,color,size){ Phone.call(this,brand,price){ this.color = color this.size = size }}
// 设置子级构造函数的原型SmartPhone.prototype = new PhoneSmartPhone.prototype.constructor = SmartPhone
// 声明子类的方法SmartPhone.prototype.photo = function(){ console.log('我可以拍照')}
SmartPhone.prototype.playGame = function() { console.log('我可以打游戏')}
const chuzi = new SmartPhone("锤子",2499,'黑色','505')1.15.2 ES6里类的继承
class Phone{ constructor(brand,price){ this.brand = brand this.price = price }
call(){ console.log('我可以打电话') }}
class SmartPhone extends Phone{ constructor(brand,price,color,size){ super(brand,price) this.color = color this.size = size } photo() { console.log('我可以拍照') } playGame() { console.log('我可以打游戏') }}
const xioami = new SmartPhone('xiaomi',2499,'黑色','505')1.15.3 子类对父类方法的重写
class Phone{ constructor(brand,price){ this.brand = brand this.price = price }
call(){ console.log('我可以打电话') }}
class SmartPhone extends Phone{ constructor(brand,price,color,size){ super(brand,price) this.color = color this.size = size } photo() { console.log('我可以拍照') } playGame() { console.log('我可以打游戏') }
// 可以对父类方法重写 call(){ console.log('我可以进行视频通话') }}
const xioami = new SmartPhone('xiaomi',2499,'黑色','505')1.15.4 class中的getter和setter
class Phone{ get price() { console.log('我被读取了') return 'i love you ' }
set price(val){ console.log('价格修改了') this.val = val }}
let s = new Phone()console.log(s.price)// '我被读取了'// 'i love you ' -------------------> 这才是s.price的返回值
s.price = 'free' // '价格修改了'1.16 数值扩展
// Number.EPSILON -------------------------- 是Javascript表示的最下精度// Number.isFinite -------------------------- 检测一个数是否为有限数// Number.isNaN -----------------------------检测一个数值是否为 NaN// Number.parseInt Number.parseFloat-------- 字符串转整数// Number.isInteger ---------------------------判断一个数是否为整数// Math.trunc ----------------------------------将数字的小数部分抹掉// Math.sign -------------------------------------判断一个数到底为正数,负数还是0 -----------> 1 -1 0
// 进制数的表示方法let b = 0b1010let o = 0o777let d = 100let x = 0xfff1.17 对象方法扩展
// Object.is --------------------------------> 判断两个值是否完全相等 console.log(Object.is(NaN,NaN)) // true console.log(NaN === NaN) // false// Object.assign ----------------------------> 对象的合并// Object.setPropertyOf ----------------------> 设置Object原型对象// Object.getPropertyOf-----------------------> 获取对象的属性1.18 模块化
// 模块化是指将一个大的文件,拆分成许多小的文件,然后将小文件组合起来,一个个小文件就是一个模块。
// 防止命名冲突 // 代码复用 // 高维护性1.18.1 ES Module
//第一种暴露方法:分别暴露export let a='zzl'; //向外暴露export function fun(){ console.log('我是谁');//向外暴露}
//第二种暴露方法:统一暴露[ 注意{}导出的并不是对象 ]let a='zzl';export{a,fun};
//第三种:默认暴露export default{ a:'zzl', fun:function(){ console.log('我是谁'); }}1.18.2 html代码引入js模块
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <script type="module"> //第一种引入方式:引入mi.js暴露的模块 import * as m from './模块化/m1.js'; console.log(m.a); //zzl
//第二种引入方式解构赋值形式:引入mi.js暴露的模块 import {a as a1,fun} from './模块化/m1.js'; console.log(a1);//可以给a一个别名a1 输出zzl //默认暴露的引入比较特殊,引入语法是
import * as m from './模块化/m1.js'; m.default.fun(); //默认暴露的解构赋值
import{default as mm} from './模块化/m1.js'; console.log(mm); //简便形式 针对默认暴露
import m from './模块化/m1.js'; //动态调用模块,即使用时再导入
import('./模块化/m1.js').then(module =>{ module.fun(); }) </script></body></html>1.18.3 module.exports 与 exports的使用与区别
// 二者都是专门负责导出的东西,都属于commonJS规范,是同步加载的.
// exports a.js const a=10
//导出 (实际上exports就是一个对象) exports._a = a
b.js
// 引入a.js导出的对象,实际就是exports对象 const ba =require('./a.js') // === exports对象 console.log('输出',ba._a) //输出 10
// exports a.js const a=10 //导出 (实际上exports就是一个对象) // exports._a=a /** * 实际上module也是一个对象 * 下边三行代码具体逻辑如下: * 1、module.exports = exports 即4行的exports, * 此时是引用赋值,并且可以继续使用原来b.js的导入方式 * 2、重新给module.exports一个新对象地址{_a:a} * 3、b.js导入方式不变,因为只是对象地址,该对象module.exports依然是导出对象 */ module.exports = { _a:a } b.js // 引入a.js导出的对象,实际就是module.exports对象 const ba=require('./a.js') // === module.exports对象
console.log('输出',ba._a) //输出 101.18.4 require()与import()
if (true){ import * from './a.js'}
/*以上代码是错误的,因为imprt关键字不支持在这类代码里,但是require支持,因为require本质是函数,但是,require不支持在浏览器里运行,并且它是同步执行的,所以ESModule里可以用import函数来引入外部模块,并且它是异步执行的.*/
if(true){ import('./a.js').then(res=>{ console.log(res._a) }).catch(err=>{ console.log(err) })}// <script src type=”module”> 也是异步加载执行的,相当于加了async属性1.19 Babel
// 它能将js的新语法转化为旧语法,以便更好的兼容。
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head> <body> // 安装工具 babel-cli babel-preset-env browserify(打包工具) // 初始化 npm init -yes // 安装 npm i babel-cli babel-preset-env browserify -D // 因为局部安装,用 npx 执行命令 // npx babel aini.js(源文件目录) -d dist/js(打包的目标目录) --presers=babel-preset-env // 打包 npx browserify dist/js/app.js -o dist/bundle.js </body></html>2,ES7
2.1 新特性
Array.prototype.includes // 检测数组中是否包含某个元素 const name = ['aini','norah','dilnur'] console.log(name.includes('aini')) // true** // 幂次方 console.log(2 ** 10) // 1024 Math.pow(2,10) 一样的效果3,ES8
3.1 async函数
// async 函数的返回值为promise对象// promise对象的结果由 async函数执行的返回值决定
async function fn() { return "aini"
// 如果跑错误,则返回的是失败的promise对象 // 如果返回的是一个promise对象,结果取决于返回的promise对象的状态 }
const result = fn(); console.log(result) // promise对象3-2 await 表达式
// await 必须写在async函数中// await 右侧的表达式一般为promise对象// await 返回的是promise成功的值// await 的promise失败了,就会抛出异常,需要通过try...catch捕获处理
const p = new Promise((resolve,reject) => { resolve('用户数据') })
async function fn() { const result = await p console.log(result) // '用户数据' }
fn()
// --------------------------------------------------------------------------------------
async function fn() { try{ const result = await p console.log(result) // '用户数据' }catch(e){ console.log(e) }
}fn()3-3 async和await结合
const fs = require("fs");
function readWeiXue() { return new Promise((resolve,reject) => { fs.readFile('/为学.md',(err,data)=>{ if(err){ reject(err) }else{ resolve(data) } }) })}
function readChaYangShi() { return new Promise((resolve,reject) => { fs.readFile('/插秧诗.md',(err,data)=>{ if(err){ reject(err) }else{ resolve(data) } }) })}
function readGuanShuYouGan() { return new Promise((resolve,reject) => { fs.readFile('/观书有感.md',(err,data)=>{ if(err){ reject(err) }else{ resolve(data) } }) })}
async function main() { let weixue = await readWeiXue() let chayang = await readChaYangShi() let guanshu = await readGuanShuYouGan() console.log(weixue.toString()) console.log(chayang.toString()) console.log(guanxue.toString())}3-4 对象方法的扩展
Object.values() // 返回一个给定对象所有可枚举属性值的数组Object.entries() // 返回一个给定对象自身可遍历属性的数组Object.getOwnPropertyDescriptors() // 返回指定对象自身属性的描述对象4,ES9
4.1 扩展运算符和rest参数
function connect({host, port, ...user}){ console.log(host) // 127.0.0.1 console.log(port) // 3306 console.log(user) // {username: 'aini', password: 'aini'}}
connect({ host:'127.0.0.1', port:3306, username:'aini', password:'aini'})
const a = { q: 'aini'}const b = { w: 'norah'}const c = { e: 'dilnur'}const d = { r: 'zhuozi'}
// 对象的合并const name = {...a, ...b, ...c, ...d}console.log(name) // {q: 'aini', w: 'norah', e: 'dilnur', r: 'zhuozi'} ES6++ 笔记