JS深入—ES6语法

107 阅读13分钟

ES6

是JavaScript语言的下一代标准,是使得js语言可以编写复杂的大型应用程序,成为企业级开发语言

1.用let声明变量

  1. 拥有块级作用域 (不会污染全局)

也就是说如果你在大括号内用 let 定义一个变量,在外面是访问不到的,而var定义却可以

  1. 不允许重复声明(不会覆盖,会报错)
  2. 声明不提升

也就是不可以未定义就调用(因为存在暂存性死区) ,而var可以将声明提前

  1. 不与顶层对象挂钩
  • var会自动跟window关联(跟window挂上),也就是当你用var定义时,打印是可以写成window.变量名,是可以访问到的 —————————
  • 但是let定义的话(不跟window挂钩),再用 window.变量名 只会得到undefined

2.const声明常量

  1. 必须有初始值
  2. 不能重复定义
  3. 拥有块级作用域
  4. 声明不提升
  5. 不与顶层对象挂钩

问:用const修饰的变量就一定不能被改吗?

       const myobj={
            name:"kerwin",
            age:100
        }
        //myobj="dwa",这样肯定不行,不能改变
        //复杂类型,所以myobj是指向复杂类型的一个地址,那我们通过地址myobj找到对象中的key然后改变它的值
        //这样就可以
        myobj.name="tiechui"

但是如果我们用的是复杂类型,但又不希望它改变,我们就用fresze来冻结住它(冻结住对象中的每一个属性)

但是这个只能冻住一级属性,如果freeze里再有复杂类型,是冻不住的(除非你再用递归方式写一个小函数)

       const myobj=Object.freeze({
            name:"kerwin",
            age:100
        })
         myobj.name="tiechui"
         //但是你此时再这样改,就没有效果了

3.变量解构赋值

就是快速地从对象或者数组中取出成员的一个语法方式

数组的解构赋值

  1. 一个最简单的数组解构赋值
       let arr=[1,2,3]
       let[a,b,c]=arr
  1. 不借助第三个变量,实现元素交换
       let x=1;
       let y=2;
       [y,x]=[x,y]
       console.log(x,y);

比如我现在想取3这个值

    let arr=[1,2,3]
    let[,,a]=arr
    console.log(a) //3

如果数组嵌套的话,我想取2,4

  let arr=[1,[2,3,4],5,6]

   let [a,[b,,d],c]=arr
   
   console.log(b,d) //2,4
  1. 有默认值
//后端返回一个空数组
        let [x]=[]
        console.log(x)//undefined
//x有默认值,且接受一个空数组,则会显示其默认值
        let [x=1]=[]
        console.log(x) //1

        let [x=1]=[100]
        console.log(x) //100

对象的解构赋值

      let obj={
        name:"kerwin",
        age:100
       }
       
       let {name,age}=obj
       //let {age,name}=obj

       console.log(name,age)//kerwin 100
  • 这里跟数组不一样的是,name和age可以调换位置,因为它是按照key值找的 (跟顺序无关)
  • 而数组不可以,数组要对号入座

  1. 而如何让应对重命名导致的报错呢?

即给它来个新名字(重命名):新名字

        let code="AAAA"
        let res={
            code:200,
            data:"11111"
        }
        //这里重命名一下
        let {data,code:co}=res
        console.log(data,co)
  1. 如果data的value是个对象呢?如果我们此时想访问这个对象里的值呢?
      let code="AAAA"
        let res={
            code:200,
            data:{
                list:["aaa","bbb","ccc"]
            }
        }
        let {data:{list},code:co}=res
        console.log(list,co)
  1. 想拿list里面的某个元素同理
       let {data:{list:[x,y,z]},code:co}=res
        console.log(x,co)

字符串的解构赋值

      let myname="kerwin"

      let [x,y,z]=myname
      console.log(x,y,z) // k e r

      //还可以获取字符串长度
      let {length}=myname
      console.log(length) //6

4.模板字符串

原来:

      //这样写不能换行(除非在换行处加一个\反斜杠)
      //但是这样添加的话,里面的东西就是写死了的
      let oli="<li>kerwin</li>"
      //字符串拼接
      let name="tiechui"
      let lio="<li>\
        "+name+"\
        </li>"

使用模板字符串

用单引号:

      //可以换行,也不用拼接
      let name="tiechui"
      let oli=`<li>
        ${name}
        </li>`

例子:比如你想在页面的ul中插入li的示例

    let arr=["tiechui","kerwin","gangdan"]
    let newlist=arr.map(function(item){
      return `<li>
        <b>${item}</b>
        </li>`
    })
    console.log(newlist)
    let oul=document.querySelector("ul")
    //oul.innerHTML=newlist
    oul.innerHTML=newlist.join("")
    //但是数组强行展示到页面中,会强行转化为字符串,并用逗号,分割

比如只想给第一个加样式

      let newlist=arr.map(function(item,index){
      return `<li class="${index===0?`active`:``}">
        <b>${item}</b>
        </li>`
    })

5.字符串与数值扩展

字符串扩展

  1. includes函数 :判断字符串中是否存在指定字符

image.png

(包含,开头,结尾)

后面也可以带参数,表示从哪个索引开始查找

  1. repeat(数字/整数/取上)

数值扩展

  1. 支持二进制八进制十进制写法
  2. Number . isFinite有限 / isNaN

减少全局性方法,使得语言逐步模块化 image.png 3. isInteger()方法 判断一个数值是不是整数

image.png 4. 极小常量Number.EPSILON

image.png 5. Math.trunc 将小数部分抹掉,返回一个整数 console.log(Math.trunc(1.2)) //1 6. Math.sign 用来判断一个数到底是正数,负数,还是0。对于非数值,会先将其转换为数值

image.png

6.数组扩展

  1. 扩展运算符(展开运算符)(三个点...)
    let arr=[1,2,3]
    //复制一个新的
    //原来let arr2=arr.concat()
    let arr2=[...arr] 
  1. Array.from() 可以把类数组对象转化为真数组
  2. Array.of() 将一组值转化为数组,即新建数组

image.png

两个的结果区别

image.png

但是 如果传正常的数组格式,array和array.of 没有区别

  1. find findIndex() 查找到第一个符合条件的元素
   let arr=[11,12,13,14,15]

   let res=arr.find(function(item){
    return item>13
   })
   console.log(res) //14

   let res=arr.findIndex(function(item){
    return item>13
   })
   console.log(res) //3
  1. find findLastIndex(),同理
  2. fill() 填充,初始化数组/替换
  3. flat() flatMap() 扁平化

flat()破掉数组(如果是几个对象就不太行了)

  let arr=[1,2,3,[4,5,6]]
  let arr1=arr.flat()
  console.log(arr.arr1)

效果:直接二维转一维 image.png

flatMap()

image.png

7.对象扩展

  1. 对象简写

就是当key的名字和value相等时,可以进行简写

比如:name,等价于name:name

  1. 对象属性 表达式
let name="a"
let obj={
//如果想让name变成一个变量,就给它套一个中括号
    [name]:"kerwin"
}
console.log(obj)//a:“kerwin”
  1. 扩展运算符...ES6不支持
  2. 支持的是Object.assign()快速合并对象
  3. Object.is() 判断两个值的相等,相当于三等于号

与===不同的是,这个可以判断NaN===NaN为true,三等于判断出来是false

8.函数的扩展

  1. 参数默认值
  2. 剩余参数...三个点
  3. 箭头函数
  • 只有return,可以省略
  • 如果返回对象要注意加()
  • 如果只有一个参数,可以省略括号()
  • 无法访问arguments,无法new
  • 箭头函数的this指向父作用域(或者说没有this )

image.png

9.Symbol(新数据类型)

两个用途:

  • 可以防止重命名覆盖
  • 作为常量(统一传代码的一致性)

ES6引入了一种新的原始数据类型symbol,表示独一无二的值(加在对象的属性上),它属于js语言的原生数据类型之一。其它数据类型是:undefined、null、boolean、string、number、object

  1. 不能进行运算

  2. 如果非要想运算或者什么,就加上个toString()转一下

  3. 隐式转换布尔

  4. 不能for in遍历,如果用的话,只能返回普通属性

    用object.getOwnPropertySymbols(),这个可以遍历处特殊的symbol类型数据

    如果非想只用一种方法遍历出来全部,可以用Reflect.ownKeys(),把他们变成一个数组返出来

    (先通过reflect转换为普通数组,再通过foreach遍历出来)

image.png


5. 作为常量

image.png

10.lterator迭代器

一个独有的内置接口

lterator的作用 image.png 两个功能:

  • for in拿到的是索引值,for of拿到的是每个具体内容
  • 除了for of,迭代器还有转数组的功能(前提是有迭代器)console.log([...oli])跟之前Array.from(oli)

遍历

     let arr=["kerwin","tiechui","gangdaner"]
     for(let i of arr){
        console.log(i)
     }

Symbol.iterator是js内置的,可以访问直接对象[Symbol.iterator],访问的时不会生成新数组导致报错

image.png

遍历流程(本质)

       let arr=["aaaa","bbbb","cccc"]
      //把返回值赋值给一个变量然后打印出来
      let iter=arr[Symbol.iterator]()
      console.log(iter)
      //返回的就是遍历器对象↑

      //迭代器对象中有一个方法叫next
      //next方法可以依次得到你数组的元素数据
      console.log(iter.next())
      console.log(iter.next())
      console.log(iter.next())
      console.log(iter.next())

image.png

原生默认具备lterator接口的数据结构如下

  • Array
  • Set
  • Map
  • String
  • arguments对象
  • NodeList对象

11.set数据结构(类似于数组)

类似于数组,但成员的值都是唯一的,没有重复的值

所以可以用于数组去重

数组去重


1.直接写好初始数组

    let s1=new Set([1,2,3,2,3,4])
    console.log(s1)
    //转换成数组
    console.log([...s1])
    console.log(Array.from(s1))

image.png 2. 用add一个个添加创建

当然可以像上面那样直接初始写好数组,当然也可以先建立一个空的,然后再一个个添加

   let s2=new Set()
   s2.add(1)
   s2.add(2)
   s2.add(3)
   s2.add(3)
   console.log(s2)

set实例的属性和方法

   console,log(s1.size) //长度

   s1.add().add() //添加,也可以链式这样写

   console.log(s1.has()) //判断有没有某个值,true/false

   s1.delete() //删除

   s1.clear() //清空

遍历

        Set.prototype.keys() //返回键名的遍历器
        Set.prototype.values() //返回键值的遍历器
        Set.prototype.entries() //返回键值对的遍历器
        Set.prototype.forEach() //遍历每个成员

这样用:

for(let i of s2.keys()){
            console.log(i)
        }

对复杂数据类型进行去重

       let list=[1,2,2,"kerwin","kerwin",[1,2],[3,4],[1,2],{name:"kerwin"},
       {age:100},{name:"kerwin"}]

       function uni(list){
        let res=new Set()
        return list.filter((item)=>{
            //对象怎么判断,用json把它转换成字符串就可以了
            //先转字符串
            let id=JSON.stringify(item)
            //然后再判断
            if(res.has(id)){
                return false
            }else{
                //如果没有就让它加上,这样下次判断才有意义
                res.add(id)
                return true
            }
        })
       }
       console.log(uni(list))

12.Map结构(类似于对象)

类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键

创建

1. 写好初始值

       let m1=new Map([
            ["name","kerwin"],
            ["age",100],
            [{a:1},"大连"]
        ])
        console.log(m1)

2. 用set一个一个设置

        let m2=new Map()
        m2.set("name","kerwin")
        m2.set("age",100)
        console.log(m2)

方法

  • set设置
  • get获取
  • has判断有没有
  • delete删除
  • clear清除
  • size获取长度

遍历

跟 for of 或者 foreach 结合使用

  • keys()
  • values()
  • entries()

13.Proxy代理

proxy如其名,它的作用是在对象和对象的属性值之间设置一个代理,获取该对象的值或者设置该对象的值,以及实例化等等多种操作,都会被拦截住,经过这一层我们可以统一处理,我们可以认为它就是“代理器”。

方法

学proxy之前,用的是Object.defineProperties

  • 拦截对象的属性被访问和修改的时候的一个拦截函数

  • 这个方法是直接访问原对象(比如下面写的obj)

缺点:

  1. 只能拦截一个属性,如果有很多个就需要循环遍历对每一个属性进行拦截
  2. 只能拦截对象,不能拦截数组
<div id="box"></div>
    <script>
       let obj={}
       //Object.defineProperties这个方法的三个参数:
       //要被拦截的这个对象/要被修改拦截的属性/get和set的两个回调
       Object.defineProperties(obj,"data",{
        get(){
            console.log("get")
            return box.innerHTML
        },
        set(value){
            console.log("set")
            //设置dom
            box.innerHTML=value
        }
       })
       console.log(obj)
    </script>

proxy

  • 不是直接访问原对象,而是给原对象加一个代理,将来是改这个代理
  • 可以拦截好多个属性
<div id="box"></div>
    <script>
       let obj={}
       let proxy=new Proxy(obj,{
        get(target,key){
            console.log("get",target[key])
            //必须要返回东西,不然是undefined
            return target[key]
        },
        set(target,key,value){
            console.log("set",target,key,value)
        
        if(key==="data"){
                box.innerHTML=value
            }
            //这个加上后,才能真正影响原对象
            target[key]=value
        }
       })
    </script>

set获取要这样

image.png

image.png

但是这里有一个易错点:当你访问一个方法的时候会报错,因为你此时的this指向的是proxy而不是你的对象

image.png

处理方法:改变this指向

image.png

14.Reflect对象(反射)

可以用于获取目标对象的行为,它与object类似,但是更易读,为操作对象提供了一种更优雅的方式,它的方法与proxy是对应的

image.png

writeable:false不可写

enumerable:false不可配置(也就是不可修改,比如删除之类的)

有什么用,跟object有什么区别

image.png
image.png
image.png

4.

image.png

对象

image.png

数组

image.png

15.Promise对象(解决回调地狱的)

16.Generator函数(手动)

后面的await和async会更好用

是ES6提供的一种异步编程解决方案(长得像同步的)

是一个状态机,封装了多个内部状态

执行generator函数会返回一个遍历器对象,也就是说,generator函数除了状态机,还是一个遍历器对象生成函数,返回的遍历器对象,可以依次遍历generator函数内部的每一个状态

基本语法(yield暂停标记)

image.png

如果想让有产出值

image.png image.png


小应用

1.手动版本

这种写法需要手动添加,并且如果特别多的话,next那里也可能形成回调地狱

//你康这一部分,是不是很像同步代码
      function *gen(){
            let res=yield ajax("1.json")
            console.log("第一个请求的结果",res)
            let res2=yield ajax("2.json",res)
            console.log("第二个请求的结果",res2)
        }
       let g=gen()
       console.log()
       g.next().value.then(data=>{
        //console.log(data)
        g.next(data).value.then(res=>{
            g.next(res)
        })
       })

2.自动版本(运用了递归调用)

(前提:yield后面必须是promise对象)

       function *gen(){
            let res=yield ajax("1.json")
            console.log("第一个请求的结果",res)
            let res2=yield ajax("2.json",res)
            console.log("第二个请求的结果",res2)
        }
        function AutoRun(gen){
            let g=gen()
            function next(data){
                let res =g.next(data)
                if(res.done)return
                res.value.then(function(data){
                    next(data);
                });
            }
            next();
        }
        AutoRun(gen)

Class语法(类语法)

原来构造函数的写法

    function Person(name,age){
        this.name=name
        this.age=age
     }
     
     Person.prototype.say=function(){
        console.log(this.name,this.age)
     }
     let obj=new Person("kerwin",100)

     console.log(obj)

class,一种语法糖,更简便一点

       class Person {
            //原来构造函数那里的
            constructor(name, age) {
                this.name = name
                this.age = age
            }
            //原来原型上的
            say() {
                console.log(this.name, this.age)
            }
        }
        let obj = new Person("kerwin", 100)

        console.log(obj)

与symbol也可以结合

        let s=Symbol('say')
        class Person {
            //原来构造函数那里的
            constructor(name, age) {
                this.name = name
                this.age = age
            }
            //原来原型上的
            [s]() {
                console.log(this.name, this.age)
            }
        }
        let obj = new Person("kerwin", 100)

        console.log(obj)
        obj[s]()

tip:还有一种静态属性的写法,可以替代下面那个

image.png

class继承

class Student extends Person{}

super方法就是表示对一个构造函数的继承,可以直接继承父类的constructor里的,相当于给子类加了父类里的东西

image.png

有个渲染页面的实例,在20集class继承那里

Module语法(模块化语法)

1.异步加载

原来 如果在head里写的话,后面加async或者defer让script异步加载(不过更建议defer)

image.png

模块化

不过直接这样写的话,你这个1.js里面就全不能访问了 image.png

所以建议是在body那个script上写猫叫,然后用导入导出

2.私密不漏

原来

如果不想被访问到(私密),可以在名字前面加个—下划线,不过这只是个君子协定,看到这样写一般就不去访问了,但是其实理论上还是可以访问到的

模块化

  1. 在body里的script里写一个导入1.js中的方法import A1 from 单引号./1.js单引号(相对路径)
  2. 再在1.js中写一个导出export default A1

tip:

导出名字必须写对,导入无所谓

并且default只能用一次

所以可以这样写(把你需要导的框起来)

image.png

3.重名不怕

image.png

4.依赖不乱

在哪用在哪引

如果想在3.js中调用必须这样写

image.png

TIP:第二种导入导出写法

还有一个不写default的导出方法

不过这里的导入名字必须严格写

       export{
            A1,
            A2
        }
        import{A1,A2} from `./1.js`

不过这种写法导入test相同名字会有重名问题,下面是解决方案

image.png

总结就是:

image.png

另外导出这里,给每个function单独export和下面大括号一块export效果是一样的

————两种方法可以结合写

NodeJS中的模块化

image.png 但是这个一导入就是全部导入,不像es6可以部分导入(导入你想要的)

      //导出规范
      module.exports=A1
      //当然也可以直接给上面的挂exports属性
      exports.B1=B1

      //导入(这个名字随便起,就是定义了一个变量)
      const A1=require("./1.js")