Es6 重点知识点总结

1,557 阅读11分钟

Var,Let,Const 区别

var 声明
  • 变量提升机制,利用javaScript引擎,在代码预编译时,JavaScript引擎会自动将所有代码里面的var关键字声明的语句都会提升到当前作用域的顶端。

下面我们来看:⬇⬇⬇

 function person1(status) { 
             if (status) { 
                var value = "Cola" 
             } else { 
             console.log(value) // undefined 
             } 
             console.log(value) // undefined 
    } 

 function person2(status) { 
         var value; 
         if (status) { 
             value = "Cola" 
         } else { 
         console.log(value) // undefined 
         } 
         console.log(value) // undefined 
} 

经过 变量提升机制 person1的代码将变为 person2。

因此,Escript6 中为我们带了块级声明

  • 只在当前函数下声明的变量有效;
  • 在代码块和{ }括号之内有效;
let 声明
  • 块级作用域,所有外面的语句块访问不到‘
  • let是没有变量提升的;

下面我们来看:⬇⬇⬇

  //let是没有变量提升
  function person3(status) { 
             if (status) { 
                  let value = "Cola" 
             } else { 
             console.log(value) // 报错 
             } 
             console.log(value) // 报错 
    } 
    
  -----------------------------------------------------
  
      //块级作用域
      console.log(value) // 报错 
      let value = "Cola"

ECMAscript6 中还提供了const关键字声明,const声明指的是常量,常量就是一旦定义完就不能修改的值。还有一点需要注意的是,常量定义必须初始化值,如果不初始化值就会报错,下面我们来看const

const 声明
  • 块级作用域,所有外面的语句块访问不到,没有变量提升;

  • const 定义的值必须定义初始值,并且不能修改;

  • const声明对象可以修改值,但是不可以重写整个对象;

      const age; // 报错 必须定义初始值
      const age = 14age = 16;  //报错 不能修改
      
      const person = { 
          name: "Cola", 
          age: 23 
      } 
      person.age = 18 
      person = {} // 报错 不能修改对象指针
    
let 和 const没有变量提升是为什么呢?
※ 暂时死区

报错是因为用 let 定义并初始化变量语句是不会执行的。此时的value还是处于在JavaScript所谓的暂时死区(temporal dead zone)简称为TDZ。

我们来说一下 TDZ 工作原理,JavaScript引擎 在扫描代码时发现变量声明时,如果遇到var就会将它们提升到当前作用域的顶端,如果遇到let或const就会将声明放到 TDZ 中,此时访问 TDZ 中的变量就会抛出错误,只有执行完TDZ中的变量才会将它移出,然后就可以正常方法。这机制只会在当前作用域生效。

我们来看:

    console.log(value) // 'undefined'
    if (true) { 
        let value = "Cola" 
    }

此时就属于我们上面说的 暂时死区

var let const 最大的区别

var 在全局作用域声明的变量有一种行为会挂载在 window 对象上,它会创建一个新的全局变量作为全局对象的属性,这种行为说不定会覆盖到 window 对象上的某个属性,而let和const声明的变量则不会有这一行为。

我们往下看:⬇⬇⬇

    var value1 = "Cola1"
    let value2 = "Cola11"
    const value3 = "Cola111"
    console.log(window.value1) // Cola1
    console.log(window.value2) // undefined
    console.log(window.value3) // undefined

箭头函数

注意使用场景:

  • 方法中不能定义 构造函数
  • 不能够调用 arguments
  • this 会跟随父级指向,父级没有默认指向 window
  • 可以调用 call() 方法;
  • 不能通过 new 关键字调用;
  • 没有 原型
  • 适合与this无关的回调( 定时器数组的方法 等);
  • 不适合与this有关的回调( 对象的方法事件回调 );

基本写法:

//ES6 增加了箭头函数:
let func = value => value;

//如果需要给函数传入多个参数:
let func = (value, num) => value * num;

//如果需要直接返回一个对象:
let func = (value, num) => ({total: value * num});

//与变量解构结合:
let func = ({value, num}) => ({total: value * num})
// 使用
var result = func({
    value: 10,
    num: 10
})
console.log(result); // {total: 100}

//自执行函数
(function(){ 
    console.log(1) 
})()

(() => { 
    console.log(1) 
})()

扩展运算符

  • 扩展运算符( ...a ) 允许一个表达式在期望多个参数(用于函数调用)或多个元素(用于数组字面量)或多个变量(用于解构赋值)的位置扩展。

下面我们直接来看这个东西的用处:

数组中使用:

    //数组的合并
    var a = [1, 2];
    var b = [0, ...a, 3]

    //数组的分割
    var [a, ...b] = [0, 1, 2];
    console.log(b)    //[1, 2]

    //数组的拷贝
    var a = [1, 2];
    var b = [...a];

对象中使用:

    let { x, y, ...z } = {
            x: 1, 
            y: 2, 
            a: 3, 
            b: 4 
    };
    console.log(z) // {a: 3, b: 4}

以上简单的介绍了一下,都是经常用的。

symbol 属性

ES6 新增了第 7 种原始数据类型 Symbol,简单介绍一下它的使用方法及使用场景

注意:通过 Symbol 方法创建值的时候不用使用 new 操作符,原因是通过 new 实例化的结果是一个 object 对象,而不是原始类型的 symbol,并且定义的是 独一无二的值

创建Symbol类型得值

    const a = Symbol(); 
    console.log(typeof s);  // 'symbol'
    
    //可以接收参数
    const a = Symbol('111');
    
    //独一无二的值
    const a = Symbol('111'); 
    const b = Symbol('111'); 
    console.log(a === b); // false

常用方法:

Symbol.for:检测上下文中是否已经存在使用该方法且相同参数创建的 symbol 值,如果存在则返回已经存在的值,如果不存在则新建。

    const a1 = Symbol.for('111');
    const q2 = Symbol.for('111');

    console.log(a1 === q2); // true

Symbol.keyFor:返回一个使用 Symbol.for 方法创建的 symbol 值的 key。

   let A = Symbol.for('uid');
    console.log(Symbol.keyFor(A));    // "uid"

    let B = Symbol.for('uid');
    console.log(Symbol.keyFor(B));   // "uid"

    let C = Symbol('uid');
    console.log(Symbol.keyFor(C));   // undefined

以上简单列举了几个,还有其他的可以看官方文档。🤭es6.ruanyifeng.com/?search=Sym…

迭代器 (Iterator)

初步认识迭代器

  • Iterator 是一种接口,目的是为不同的数据结构提供统一的数据访问机制。也可以理解为 Iterator 接口主要为 for of 服务的,供for...of进行消费。 下面我们来看下迭代器的基本使用方法:

    const a = ['a','b','c'] let arr = aSymbol.iterator

    //调用对象next方法 console.log(iterator.next()) //{value:'a',done:false} console.log(iterator.next()) //{value:'b',done:false} console.log(iterator.next()) //{value:'c',done:false} console.log(iterator.next()) //{value:undefined,done:true}

工作原理

  • 创建一个指针对象,指向当前数据结构的 起始位置
  • 第一次调用对象的 next 方法,指针自动指向数据结构的 第一个成员;
  • 接下来不断调用 next 方法,指针自动往后移动,直到指向最后一个成员;
  • 每调用 next 方法返回一个包含 valuedone 属性的对象;

实现迭代器

下面我们来做一个迭代器: ⬇ ⬇ ⬇

    let obj = {
        name: '中级三班',
        array: [
            'eqebj',
            'daduo',
            'grguy',
            'rgrmo'
        ],
        [Symbol.iterator]() {
            let index = 0
            let _this = this
            let _done = false
            return {
                next: function () {
                    if (index < _this.array.length) {
                        const result = {value: _this.array[index], done: _done}
                        index++
                        return result
                    } else {
                        return {value: undefined, done: !_done}
                    }
                }
            }
        }
    }
    
    //遍历这个对象,使用迭代器方式
    for (let arr of obj) {
        console.log(arr)
    }
    

让我们来接着往下看,我们的 迭代器 (Interator) 还有什么用处 --- ---

解构赋值

    let obj = {
        name: '中级三班',
        array: [
            'eqebj',
            'daduo',
            'grguy',
            'rgrmo'
        ],
        [Symbol.iterator]() {
            let index = 0
            let _this = this
            let _done = false
            return {
                next: function () {
                    if (index < _this.array.length) {
                        const result = {value: _this.array[index], done: _done}
                        index++
                        return result
                    } else {
                        return {value: undefined, done: !_done}
                    }
                }
            }
        }
    }

    // 解构赋值
    let [a, b, c] = obj
    console.log(a)   //eqebj
    console.log(b)   //daduo
    console.log(c)   //grguy

好啦,目前这几种比较常用,大家要记住哦!😊 ---偷偷告诉你们面试会加分的哦!(●ˇ∀ˇ●)

哎呦!突然还想起来个小知识点,for...in 和for...of 的区别:

  • for in 循环 --- 键名
  • for of 循环 --- 键值

生成器 (generator)

下面又到了我们的生成器了,我们继续来看 💪💪💪

概念:

  • Generator 函数是 ES6 提供的一种 异步编程 解决方案,语法行为与传统函数完全不同;
  • 语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态
  • Generator 函数除了 状态机,还是一个 遍历器对象生成函数
  • 暂停函数(惰性求值), yield 可暂停,next方法 可启动。每次返回的是yield后的表达式结果;

主要作用:解决回调地域问题 --- (异步)

下面我们来看使用方法:⬇ ⬇ ⬇

基本使用

Generator 函数是分段执行的,调用 next方法 函数内部逻辑开始执行,遇到 yield 表达式停止,返回{value: yield后的表达式结果/undefined, done: false/true},再次调用 next 方法会从上一次停止时的 yield 处开始,直到最后。

    function* fun() {
      yield '111';
      yield '222';
      return '333';
    }
    var h = fun();
    h.next()// { value: '111', done: false }
    h.next()// { value: '222', done: false }
    h.next()// { value: '333', done: true }
    h.next()// { value: undefined, done: true }

next 传递参数

yield 表达式本身没有返回值,或者说总是返回 undefinednext方法可以带一个参数,该参数就会被当作上一个 yield 表达式的返回值。

    function* fun () {
      console.log('开始执行')
      let result = yield '111'
      console.log(result)
      yield '222'
    }
    
    let M = fun()
    M.next()
    M.next(11)
   
    //执行结果:
    
    // 开始执行
    // 11
    // {value: "222", done: false}

与迭代器的关系

由于 Generator 函数就是遍历器生成函数,因此可以把 Generator 赋值给对象的Symbol.iterator属性,从而使得该对象具有 Iterator 接口

    let obj = { 
        name: 'Cola',
        age: 19 
    }
    
    obj[Symbol.iterator] = function* fun() {
      yield 1;
      yield 2;
      yield 3;
    };
    
    for (let i of obj) {
      console.log(i)  // 1 2 3
    }

Generator 函数赋值给 Symbol.iterator 属性,从而使得obj对象具有了 Iterator 接口,可以被for of遍历了。

promise 异步

Promise的构造函数接收一个参数:函数,并且这个函数需要传入两个参数:

  • resolve :异步操作执行成功后的回调函数
  • reject:异步操作执行失败后的回调函数

认识 promise

    let p = new Promise((resolve, reject) => {
        //异步回调
        setTimeout(() => {
            console.log('执行完成');
            resolve('我是成功!!');
        }, 2000);
    });
    

promise 是用来解决两个问题的:

  • 回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象;
  • promise 可以支持多个 并发 的请求,获取并发请求中的数据;
  • 这个promise可以解决 异步 的问题,本身不能说promise是异步的;

promise封装ajax请求

我们每学习一点都要应用到 实战 中,接下来我们看 promise封装异步ajax

    const p = new Promise((resolve, reject) => {
        //创建对象
        let xml = new XMLHttpRequest()
        // 初始化
        xml.open('get', '/data/person.json')
        // 发送请求
        xml.send()
        //绑定事件处理 回调函数
        xml.onreadystatechange = function () {
            if (xml.readyState === 4) {
                if (xml.status >= 200 && xml.status < 300) {    // 请求成功
                    resolve(xml.response)
                } else {    // 请求失败
                    reject(xml.status)
                }
            }
        }
    })

    p.then(function (value) {
        console.log(value)
    }, function (reason) {
        console.error(reason)
    })

以上就是我们经常用的异步的发送请求,大家有空都联系一下。。。

then 的链式操作

多个请求 要一起请求或者一次性 请求多个文件 的时候,用普通的方法很可能或造成回调地狱,因此,就可以用链式操作来防止一次性请求多个文件 回调地狱 的发生。

下面我们来看这个例子:

    const fs = require('fs')
    const p = new Promise((resolve, reject) => {
        fs.readFile('./data1.md', (err, data) => {
            if (true) resolve(data)
            else reject(err)
        })
    })

    p.then(value => {
        return new Promise((resolve, reject) => {
            fs.readFile('./data2.md', (err, data) => {
                if (true) resolve([value, data])
                else reject(err)
            })
        })
    }, reason => {
        console.error(reason)
    }).then(value => {
        return new Promise((resolve, reject) => {
            fs.readFile('./data3.md', (err, data) => {
                if (true) {
                    value.push(data)
                    resolve(value)
                } else {
                    reject(err)
                }
            })
        })
    }, reason => {
        console.error(reason)
    }).then(value => {
        console.log(value.toString())
    }, reason => {
        console.error(reason)
    })

这个例子用到了 nodejs配置 ,应用的时候可以试着配一下!!! 这部分我们就先介绍这几点,还有 promise.all,promise.race,promise.any,promise.try 等其他的函数,没事的时候可以看下官方文档:es6.ruanyifeng.com/#docs/promi…

接下来我们继续看:⬇ ⬇ ⬇

集合Map和Set

set

Set 本身是一个构造函数,用来生成 Set 数据结构。Set 函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。Set 对象允许你存储任何类型的值,无论是原始值或者是对象引用。它类似于数组,但是成员的值都是唯一的,没有重复的值。

add,delete,has

  • add(value):添加某个值,返回 Set 结构本身(可链式调用)。

  • delete(value):删除某个值,删除成功返回 true,否则返回 false

  • has(value):返回一个布尔值,表示该值是否为 Set 的成员。

  • clear():清除所有成员,没有返回值。

      let set = new Set()
      set.add('123')
      set.add('42')
      set.add('424')
      console.log(set)  // { '123', '42', '424' }
      set.delete('123')
      console.log(set)  //{ '42', '424' }
      console.log(set.has('42'))  // true
    
  • keys():返回键名的遍历器;

  • values():返回键值的遍历器;

  • entries():返回键值对的遍历器;

  • forEach():使用回调函数遍历每个成员;

由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以 keys 方法和 values 方法的行为完全一致。

接下来我们来简单的实现几个例子:

去重,交集,并集,差集

    let s = ['123', '432', '424', '424', '123', '432']
    let s1 = ['313', '442', '123', '424']

    // 去重
    let result = new Set(s)
    console.log(Array.from(result))  //[ '123', '432', '424' ]

    // 交集
    let array1 = Array.from(new Set(s)).filter(index => Array.from(new Set(s1)).indexOf(index) > -1)
    console.log(array1)  //[ '123', '424' ]

    // 并集
    let array = Array.from(new Set([...new Set(s), ...new Set(s1)]))
    console.log(array)    //   [ '123', '432', '424', '313', '442' ]

    //差集
    let array2 = Array.from(new Set(s)).filter(index => Array.from(new Set(s1)).indexOf(index) === -1)
    console.log(array2)  //[ '432' ]

以上就是set的常用方法,下面我们继续来看。

map

Map对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。构造函数Map可以接受一个数组作为参数。

set,add,delete,get,size,clean

  • size:返回Map对象中所包含的键值对个数

  • add:向Map中添加新元素;

  • set(key, val): 向Map中添加新元素;

  • get(key): 通过键值查找特定的数值并返回;

  • has(key): 判断Map对象中是否有Key所对应的值,有返回true,否则返回false;

  • delete(key): 通过键值从Map中移除对应的数据;

  • clear(): 将这个Map中的所有元素删除;

     const m1 = new Map([['a', 111], ['b', 222]])
     console.log(m1) // {"a" => 111, "b" => 222}
     m1.get('a')  // 111
    
     const m2 = new Map([['c', 3]])
     const m3 = new Map(m2)
     m3.get('c') // 3
     m3.has('c') // true
     m3.set('d', 555)
     m3.get('d') // 555
    

遍历方法

  • keys():返回键名的遍历器

  • values():返回键值的遍历器

  • entries():返回键值对的遍历器

  • forEach():使用回调函数遍历每个成员

    const map = new Map([['a', 1], ['b',  2]])
    
    for (let key of map.keys()) {
      console.log(key)   // 'a' 'b'
    }
    
    for (let value of map.values()) {
      console.log(value) //1   2
    }
    
    for (let item of map.entries()) {
      console.log(item)       // ['a', 1]  ['b', 2]
    }
    
    // for...of...遍历map等同于使用map.entries()
    for (let [key, value] of map) {
      console.log(key, value)    // 'a' 1     'b' 2
    }
    

es7 新特性

inclued() 函数

inclued() : 函数用来判断一个数组是否包含一个指定的值,如果包含则返回 true,否则返回false

    let arr = ['123','234','345']
    let x = '123'
    arr.includes(x)   //true

指数操作符

在ES7中引入了指数运算符****具有与Math.pow(..)等效的计算结果。

console.log(Math.pow(2, 10)); // 输出1024
console.log(2**10);// 输出1024

es8 新特性

async...await

ES2018引入异步迭代器(asynchronous iterators),这就像常规迭代器,除了next()方法返回一个Promise。因此await可以和for...of循环一起使用,以串行的方式运行异步操作。例如:

    async function process(array) {
      for await (let i of array) {
        doSomething(i);
      }
    }

keys,values,entries,getOwnPropertyDescriptors

  • Object.values()
  • Object.values()是一个与Object.keys()类似的新函数,但返回的是Object自身属性的所有值,不包括继承的值;
  • Object.entries()函数返回一个给定对象自身可枚举属性的键值对的数组;
  • Object.getOwnPropertyDescriptors()函数用来获取一个对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象。

这篇的知识点不少啦🤭,看到这里大家应该都累了,注意休息哈,下篇随后就出来,加油哦!努力的程序小猿们。