js整理(7)

88 阅读9分钟

ES6新特性

(一)变量的解构赋值
ES6允许按照一定模式,从不同类型数据中提取值,然后对变量进行赋值,这杯称为「解构赋值」。
数组的解构赋值

let [a, b, c] = [123]

可以从数组中提取值,按照对应位置,对变量赋值。
对象的解构赋值
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值

        let {name,age}={age:10,name:"小花"}
        console.log(name,age);
        输出:小花 10
        
        let x = 1;
        let y = 2;
        [x,y]=[y,x]
        console.log(x,y);
        输出:2 1

对象的解构也可以指定默认值:

        let {name,age=10}={name:"小花"}
        console.log(name,age);
        输出:小花 10
        

函数参数的解构赋值

    function add([x, y]){
    return x + y
    }
    add([1, 2]) // 3

(二)展开运算符
展开运算符,也叫扩展运算符,写法:三个点(...)
(1)展开字符串

let str = 'app'
console.log(...str) // a p p,将字符串展开成一个个的单个字符
console.log([...str]) // ["a", "p", "p"]
console.log([...str].reverse().join('')) // ppa,将字符串倒序

(2)展开数组

        let arr=["a","b","c"]
        console.log(...arr);
        输出:a b c

应用场景:
1.数组浅拷贝

        let arr=["a","b","c"]
        let arr2=[...arr]

2.数组合并

        let arr=["a","b","c"]
        let arr1=[1,2,3,4,5]
        let arr2=[...arr,...arr1,"小花"]
        console.log(arr2);
        输出: ['a', 'b', 'c', 1, 2, 3, 4, 5, '小花']

3.伪数组转为真数组

    function sum() {
    console.log([...arguments]) // [1, 2, 3, 4, 5]
    }
    sum(1, 2, 3, 4, 5)

(3)展开对象
应用场景:
1.对象浅拷贝

        let obj={name:"小花",age:19}
        let obj1={...obj}

2.对象的合并

        let obj={name:"小花",age:19}
        let obj1={sex:1}
        let obj2={love:"读书",...obj,...obj1}
        console.log(obj2);
        输出:{love: '读书', name: '小花', age: 19, sex: 1}

(三)模板字符串
模板字符串是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量:

        let obj={name:"小花",age:19}
        let str=`我叫${obj.name},今年${obj.age}`
        console.log(str);
        输出:我叫小花,今年19

(四)对象的简洁表示

// ES5 写法
let obj1 = {
name: name,
show: function() {
console.log('我是show函数')
}
}

// ES6 写法
let obj2 = {
name, // key: value,如果 key 和 value 相同,省略
show() { // 省略 : function
console.log('我是show函数')
}
}

(五)class

    // 类
    class Person {
    // 构造器
    constructor(name, age) {
    this.name = name
    this.age = age
    }

    // 原型方法
    show() {
    console.log('我是原型方法')
    }

    // 静态方法
    static say() {
    console.log('我是静态方法')
    }
    
    // 创建实例对象
    let yuan = new Person('小花', 10)

注意:(1)constructor方法:constructor方法就相当于ES5写法的构造函数,该方法是类的默认方法,通过new命令生成实例对象时会自动调用它。即便没有显示地定义它,一个空的constructor方法也会被默认添加;
(2)静态方法:ES6的class中通过在方法前加static关键字来表明该方法是一个静态方法,从而可以直接在类上调用;
es6继承改写ex5寄生组合式继承

ES5
        function Father(name, age) { // 父类
            this.name = name
            this.age = age
            this.skills = ['耍', '睡觉', '吃饭']
        }
        Father.prototype.getFatherSkill = function() { // 父类方法-获取父类技能
             return this.skills
        }

        function Son(name, age) { // 子类
        // 1.调用父类构造函数,从而继承父类的属性
        Father.call(this, name, age)
        // 可以继续定义子类的属性
        this.love = '读书'
        }
        // 2.继承父类原型上的方法
        Son.prototype = Object.create(Father.prototype)
        // 3.完善子类的 constructor 指向
        Son.prototype.constructor = Son

        // 调用
        const son = new Son('小花', 5)
        son.skills.push('装死')
        console.log(son.skills) // ['耍', '睡觉', '吃饭','装死']

        const twoson = new Son('小于', 2)
        console.log(twoson.skills) // ['耍', '睡觉', '吃饭']
ES6
     class Father { // 父类 
            constructor(name, age) {
            this.name = name
            this.age = age
            }
            skills = ['耍', '睡觉', '吃饭']
            getFatherSkill() { // 原型方法
            console.log(this.skills)
            }
        }

        class Son extends Father { // 子类 
            constructor(name, age) {
            // ES6 要求,子类的构造函数必须执行一次super函数。
                    super(name, age) // 调用父类构造函数。
            }
            love = '读书'
        }

        const son = new Son('小花', 5)
        console.log(son.skills)  // ['耍', '睡觉', '吃饭','装死']
        son.skills.push('装死')
        const twoson = new Son('小于', 6)
        console.log(twoson.skills)   // ['耍', '睡觉', '吃饭']

(六)ES6模块化
ES6之前没有模块化,但需要模块化,需要它主要是要解决2个问题: (1)命名冲突; (2)需要各个代码块职责分离,高内聚、低耦合,这样维护性和可读性更高;
主要的优势是「静态加载」

语法
模块功能主要由两个命令构成:import和export: (1)import命令用于导入其他模块提供的功能; (2)export命令用于规定模块的对外接口;

单文件导出

导出:
export 要导出的内容1
export 要导出的内容2
export 要导出的内容3
导入:
import { 导入的内容1, 导入的内容2, 导入的内容3 } from '文件模块路径'

整个文件导出

导出:
export default 要导出的内容
导入:
import 变量 from '文件模块路径'
示例:
a.js:这个模块负责导出

const num = 100
const name = '小明'
const fn = () => {}
const arr = ['a''b''c']
export default {
num,
name,
fn,
arr
}

b.js:这个模块负责导入,并使用
import obj from './a.js'
const { num, name, fn, arr } = obj
console.log(num, name, fn, arr)

ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量,加载效率要比运行时高得多.

(七)Set和Map数据结构

Set和Map是ES6新增的数据结构(或者叫类,因为它们也是构造函数)。 Set类似于数组,但Set里的元素是唯一的,不会重复。

const s = new Set([1, 2, 3, 3, 2, 1])
const arr = [...s] // Set 也可以使用展开运算符!
console.log(arr) // [1, 2, 3]

Set实例的常用属性: (1).size:实例的元素数;
Set实例的常用方法: (1).add(value):添加元素 (2).delete(value):删除元素 (3).clear():清除所有元素 (4).has(value):判断是否有某个元素

let s = new Set([123321])
console.log(s) // {1, 2, 3}
console.log(s.size// 3
s.add(3// s 为 {1, 2, 3},已有的值不会被重复添加
s.add(4// s 为 {1, 2, 3, 4}
console.log(s.has(4)) // true, 判断是否有某个元素
s.clear() // s 为 {}

Map类似于对象,也是键值对的集合,但“键”的范围不仅限于字符串,任意类型值(包括对象)都可以作为键:

// Map 构造函数接收包含表示键值对的嵌套数组
const m = new Map([
['str', 'String'],
[[1, 2, 3], 'Array'],
[{}, 'Object'],
[false, 'Boolean']
])
console.log(m)
// {"str" => "String", Array(3) => "Array", {…} => "Object", false => "Boolean"}

Map实例的常用属性:
(1).size:实例的元素数;
Set实例的常用方法:
(1).set(key, value):设置key所对应的键值,返回整个Map结构。若key已有值,则键值会被更新;
(2).get(key):取key对应的值,若找不到key则返回undefined
(3).delete(key):删除某个键,删除成功返回true,失败返回false
(4).clear():清除所有成员
(5).has(key):判断某个键key是否在Map实例数据结构中

const m = new Map([
[1, 'Number']
])
console.log(m.size) // 1
m.set([], 'Array') // m 为 {1 => "Number", Array(0) => "Array"}
console.log(m.get(1)) // 'Number'
console.log(m.has({})) // false
m.delete(1) // m 为 {Array(0) => "Array"}
m.clear() // m 为 {}

(八)异步编程
同步:代码从上往下依次执行,后面的代码要等到前面的代码执行完之后,才能执行;
异步:代码从上往下依次执行,遇到异步代码,异步代码会先让开,在一边等待,等所有同步代码都执行完,再执行异步代码;
JS中常见的异步场景
(1)定时器
(2)事件处理函数
(3)异步Ajax请求
(4)回调函数
(九)Promise
回调地狱:进行第一次请求,得到用来第二次请求的数据,再进行第二次请求,得到 第三次请求的数据....一旦嵌套超过2层,这“坨”代码就变得非常难以阅读了。这种回调函数中嵌套着回调函数的现象,称之为「回调地狱」。
Promise是用来「管理异步操作」的,用同步的方式来编写异步代码,用来解决回调地狱问题的。 (1)Promise的状态
初始化,状态:pending
当调用resolve(成功),状态:pengding=>fulfilled
当调用reject(失败),状态:pending=>rejected
Promise是一个内置的构造函数,用“new Promise构造函数”的形式来生成Promise实例

// 创建 Promise 的实例对象
new Promise((resolve, reject) => {
// 可以写很多逻辑代码,同步或者异步都可以
if (true) { // 假设异步操作成功
// 改变状态:进行中 pending -> 已成功 fulfilled
resolve('成功的数据')
} else {
// 改变状态:进行中 pending -> 已失败 rejected
reject('失败的数据')
}
})
.then((data) => {
// data 是上一步 resolve 传入的数据
console.log(data)
})
.catch((err) => {
// 上一步 reject 传入的数据,也可以捕获之前所有 then() 方法执行的错误
console.log(err)
})

Promise构造函数接收一个函数作为参数,该函数的两个参数又是函数,分别是resolve和reject: (1)resolve()函数:改变Promise对象状态,进行中 -> 已成功,并将异步操作结果作为参数传进去; (2)reject()函数:改变Promise对象状态,进行中 -> 已失败,将异步操作的报错作为参数传进去;

Promise实例生成后,可以用then()方法指定已成功和已失败时的回调: 注意,虽然then()函数的第2个参数和catch()都能拿到已失败的数据,但一般推荐用catch,因为catch能够捕获它之间所有出现的then中的错误。

Promise来改写回调地狱的例子
        new Promise((resolve) => {
            $.ajax({
            url: 'url1',
            type: 'get',
            success: (data) => {
            // 请求成功时,调用 resolve() 将状态改为已成功,并传递成功数据
            resolve(data)
                 }
            })
        })
            .then((data) => { // 状态变为已成功时执行回调,拿到成功数据
            const { id } = data
            return new Promise((resolve) => { // 返回一个新的 Promise,来使结果继续传递
                $.ajax({
                url: 'url2',
                type: 'get',
                data: { id },
                success: (data) => {
                resolve(data)
                }
                })
            })
            })
            .then((data) => {
            const { code } = data
            $.ajax({
            url: 'url3',
            type: 'get',
            data: { code },
            success: (data) => {
            console.log('终于拿到', data)
            }
            })
            })
// 封装请求数据的逻辑
const getData = (url, params = {}) => {
return new Promise((resolve) => {
    $.ajax({
    url,
    type: 'get',
    data: params,
    success: (data) => {
    // 请求成功后把数据传递给下一个 .then()
    resolve(data)
    }
})
})
}

getData('url1')
.then((data) => getData('url2', { id: data.id }))
.then((data) => getData('url3', { code: data.code }))
.then((data) => {
console.log('终于拿到', data)
})

(十)async与await
async关键字写在函数前面,表示该函数中有异步操作:await只能使用在async修饰的函数中,用于等待异步操作结果(一般等待的是一个Promise的状态改变)。

      let timer=()=>new Promise((resolve)=>{
                setTimeout(()=>{
                    resolve("异步操作1")
                    console.log("异步操作");
                },1000)
             }
            )
        async function test(){
           let res=await timer()
           console.log(res);
           console.log(234);
        }
        test()
        console.log(1);
        输出:1;异步操作;异步操作1234
        
        
        function timer(){
          return   new Promise((resolve)=>{
                setTimeout(()=>{
                    console.log("异步操作");
                    resolve("异步操作1")
                },1000)
            })         
        }
        async function test(){
           let res=await timer()
           console.log(res);
           console.log(234);
        }
        test()
        console.log(1);
        输出:1;异步操作;异步操作1234