导读
本篇文章包括let变量声明、const声明常量、解构赋值、模版字符串、对象简化、箭头函数、rest参数、扩展运算符、Symbol、迭代器、手写迭代器、生成器、Promise、数值扩展、Set、模块化
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();//可以拿到 }
const声明常量
- 一定要赋初始值
- 常量的值不能再修改
- 也是块级作用域
//声明常量 const CL='zzl'; // 对于数组和对象的元素修改,不算2中的常量修改,不会报错 const SZ=['zzl','wxl']; SZ.push('ll');//此时不会报错,因为只是修改数据,并没有修改地址
解构赋值
//数组解构赋值 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();
模版字符串
let str=`字符串`
- 它可以支持换行
- 变量拼接
let name='zzl'; let data=`${name}是个人` //输出 zzl是个人
对象简化写法
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('我会动'); } }
箭头函数
// 声明一个函数 let fn=function(a,b){ } // 箭头函数声明一个函数 let fn=(a,b)=>{ } fn(1,2);
特点
// 箭头函数的额this是静态的,this始终指向函数声明时所在作用域下的this的值 function getname(){ //name是zzl,直接调用的话this值指向window } console.log(this.name);//zzl let getname2=()=>{ //而这个箭头函数是在全局作用域下声明的,所以this也是指向window console.log(this.name);//zzl } window.name='zzl'; const data={ name:'wxl' } //直接调用 getname();//name是zzl,直接调用的话this值指向window getname2();//而箭头函数是在全局作用域下声明的,所以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);//会报错,不用用来保存实参
简写箭头函数
// 省略小括号 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));
箭头函数注意事项
- 箭头函数适合与this无关的回调,比如定时器,数组的方法回调。
- 箭头函数不适合与this有关的回调,比如DOM元素的事件回调、对象的方法。
btn.addEventListener("click",function(){ //此时普通函数的this指向事件缘 //如果使用箭头函数,事件源将变成外部作用域的this值,即这个函数所在的作用域 })
函数参数初始值
function add(a,b,c=10){ } //函数参数初始值与结构赋值结合使用 function data({name,age='22'}){ console.log(name); console.log(age); } data({ name='zzl', // age='23' })
rest参数
用于获取函数的实参,可以代替arguments。
// function data(){ // console.log(arguments);//输出的是一个对象 // } // data('zzl','wxl'); // rest参数 function data(...args){ console.log(args); //输出的是一个数组,可以使用filter some... } data('zzl','wxl');
function data(a,b,...args){ console.log(a);//1 console.log(b);//2 console.log(args);//345 } 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'}
扩展运算符…
它能将数组转换为逗号分隔的参数序列
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];//转为真正的数组
Symbol
它一种新的数据类型,表示独一无二的值,类似于字符串的数据类型。
- 它的值是唯一的,用来解决命名冲突的问题。
- 它的值不能于其他数据进行运算。
- 它定义的对象属性不能使用for…in 循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名。
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' }
迭代器
任何数据结构只要部署了Iterator接口,就可以使用for…of 来遍历.
具备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这个属性。
工作原理
- 先创建一个指针对象,指向当前数据结构的起始位置
- 第一次调用对象的next方法,指针自动指向数据结构的第一个成员
- 接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员
- 每次调用next方法就会返回一个包含value和done属性(是否完成)的对象
手写迭代器
//声明一个对象 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);结果演示
数组的常用方法
forEach
缺点:找到目标还会继续循环,并且不受return和break的影响。
some
找到目标后,可以通过return结束循环。
const arr=[] arr.some((item,index)=>{ if(item=='zzl'){ console.log(item) return true } })
every
只要每一项都满足every里面的判断条件,则最终返回true。
const arr=[ {id:1,state:true}, {id:2,state:true} ] //判断数组中的所有state是否都为true const result=arr.every(item=>item.state)
filter
const data=arr.filter(item=>item.state)
filter会把满足条件的数据重新过滤到的新数组中。
reduce
arr.filter(item=>item.state).reduce(累加的结果,当前循环项)=>{ },初始值)
第一次循环累加的结果默认等于初始值,以后循环累加的结果等于上一次累加的结果加上循环项里的数据。
const arr=[{id:1,state:true,price:10}] let sum=0 arr.filter(item=> item.state).reduce((sum,item)=>{ return sum+= item.price //return给下一次累加使用 },0)
生成器
生成器就是一个特殊的函数,是异步编程新的解决方案。分别通过以下代码可知运行逻辑。
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();//输出zzl a.next();//输出wxl
function * 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}
生成器函数的参数传递
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')) console.log(a.next('ddd'))
解决回调地狱
// 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();结果演示
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(); //用户数据 作为第一个yield的整体返回结果 console.log(users); let orders= yield two(); console.log(orders); let goods= yield three(); console.log(goods); } //调用生成器函数 let a=fun(); a.next();结果演示
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); } )
数值扩展
console.log(0.1+0.2===0.3);//不等于 输出false function bj(a,b){ if(Math.abs(a-b)<Number.EPSILON){ //a-b的差值小于这个误差,就认为你们相等 return true; }else{ return false; } } console.log(bj(0.1+0.2,0.3));//输出true,0.1+0.2===0.3
Set
类似于数组,但是成员的值是唯一的,即自动去重。
let arr=[1,2,3,4,3,2,1]; //数组去重 let result=[...new Set(arr)];//[1,2,3,4] //交集 let arr2=[1,4,5,6,5,6]; let result2=[...new Set(arr)].filter(item=>{ let s2=new Set(arr2);//[1,4,5,6] if(s2.has(item)){ return true; }else{ return false; } }) console.log(result2);//[1,4]; //并集 let arr3=[...new Set([...arr,...arr2])] console.log(arr3);//[1,2,3,4,5,6] //差集,交集的逆运算 let arr2=[1,4,5,6,5,6]; let result2=[...new Set(arr)].filter(item=>{ let s2=new Set(arr2);//[1,4,5,6] if(!s2.has(item)){//item不在s2里面 return true; }else{ return false; } }) console.log(result2);//[1,4]
模块化
模块化是指将一个大的文件,拆分成许多小的文件,然后将小文件组合起来,一个个小文件就是一个模块。
- 防止命名冲突
- 代码复用
- 高维护性
ES Module
- export命令用于规定模块的对外接口(向外暴露)
- import命令用于输入其他模块提供的功能
js模块代码
//第一种暴露方法:分别暴露 export let a='zzl'; //向外暴露 export function fun(){ console.log('我是谁');//向外暴露 } //第二种暴露方法:统一暴露[ 注意{}导出的并不是对象 ] let a='zzl'; export{a,fun}; //第三种:默认暴露 export default{ a:'zzl', fun:function(){ console.log('我是谁'); } }
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>
<script src="文件a" type="module"><script>
以便减少一个主文件的代码量
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
module.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) //输出 10
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属性
Babel
它能将js的新语法转化为旧语法,以便更好的兼容。