ES6入门教程

本文详细介绍了JavaScript ES6的关键特性,包括let和const、Symbol、解构赋值、字符串扩展、数组扩展与运算符、Map与Set、Proxy与Reflect、类与继承、异步调用(Promise和async/await)。通过实例演示了这些特性的使用,帮助读者掌握ES6在实际开发中的应用。
摘要由CSDN通过智能技术生成

 目录

简介

创建项目

使用babel

关键字 let、const

symbol数据类型

解构赋值

字符串的扩展方法以及模板字符串

数组扩展与运算符扩展

map、reduce

语法糖

Map、WeakMap

Set、WeakSet

Map、Set、Array、Object 的区别

代理Proxy、反射Reflect

函数参数默认值

函数参数可变长度

函数尾调用(替换递归)

箭头函数

类的 get set

类的静态方法

类的静态属性

类的继承

遍历对象

js 模块化( import 、export )

方式一

方式二

异步调用

Promise

Generator

async await


简介

  • ECMAScript 6是什么
    • ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,之前的JavaScript 标准是ES5,ES6在2015年6月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
  • ECMAScript 和 JavaScript 的关系
    • ECMAScript是JavaScript的标准, JavaScript是ECMAScript的实现。换句话说,如果说JavaScript是一门语言的话,那么ECMAScript就是这门语言遵循的语法书。

创建项目

新建文件夹 ES6,在VScode中打开(VScode安装插件 Chinese (Simplified)、vscode-icons、Live Server)

新建两个文件夹 src用于存放es6语法的代码,dist用于存放编译后的代码

打开VScode终端,使用命令

//意思是使用默认配置初始化项目
npm init -y


使用babel

安装 babel  终端使用命令(babel的作用是把es6的代码转译成es5)

// npm安装 非全局安装 babel客户端,babel的规则集,使用es2015(es6转换代码)
npm i --save-dev babel-cli babel-preset-es2015

测试 babel是否生效

配置babel,在根目录下新建.babelrc文件,编写转译规则集

在src目录下新建一个test.js文件,使用es6语法写一行代码

使用babel编译test.js,命令如下

// 非全局的babel所以要在目录中使用babel命令   es6文件  输出    输出位置
.\node_modules\.bin\babel .\src\test.js --out-file .\dist\test.js

 发现报错,具体如下

 解决办法,使用管理员运行VScode,执行命令

PS D:\Users\lixx\Desktop\es6> get-ExecutionPolicy
Restricted
PS D:\Users\lixx\Desktop\es6> set-ExecutionPolicy RemoteSigned
PS D:\Users\lixx\Desktop\es6> get-ExecutionPolicy
RemoteSigned

 重新执行babel命令,发现在dist目录下生成了一个转译后的文件,转译成功

 转译多个文件使用文件夹命令

 自动转译


关键字 let、const

var在声明变量时的全局问题

// 在 for中声明的变量,我们在for结束后还可以使用输出的结果是10,造成内存泄漏问题
for (var i = 0; i<10; i++) {}
console.log(i);

 var在声明变量时的局部作用域问题

// 会输出 undefined,按照我们原本的设想应该报Uncaught ReferenceError: b is not defined错误
!(function(){
    console.log(b);
    var b = 10;
})()

// 原因是浏览器在执行的时候会执行js变量提升的操作行为,会把代码转换成以下进行执行,可能造成我们预期之外的结果
!(function(){
    var b;
    console.log(b);
    b = 10;
})()

块级作用域 { } 

因为有上面的问题,所以在es6中出现了 块级作用域 { } 在花括号中定义的变量在外部是不能访问的

  • let: 用来声明变量
  • const: 用来声明一个只读的变量(值可以变,保证内存地址不可变)
    • 变量声明的同时必须⽴即赋值
    • 如声明的是简单类型的数据,变量的值不可改变
    • 简单类型如字符串、数字和布尔值,值就保存在变量指向的内存地址。⽽复杂类型的数据如 对象、数组和函数,保证变量的引用地址是不变的,但是对象的堆内存地址的数据是会改变的

特点:只在声明的代码块中生效、暂时性死区、没有变量提升、不能重复声明


symbol数据类型

ES6之前JavaScript的数据类型

  • Number(数字)
  • String(字符串串)
  • Boolean(布尔值)
  • Object(对象)
  • Null(空对象指针)
  • Undefined(声明的变量量未被初始化时)

出现原因

  • 对象的属性名容易产生命名冲突,为保证键名的唯一性,故es6引入Symbol这种新的原始数据类型,确保创建的每个变量都是独一无二的

用法

  • Symbol类型的数据是类似字符串的数据类型,由于Symbol函数返回的值是原始类型的数据,不是对象,故Symbol函数前不能使用new命令,否则会报错。
let a1 = "a";
let a2 = "a";
console.log(a1 === a2);   // true

let b1 = Symbol("b");
let b2 = Symbol("b");
console.log(b1 === b2);   // false

let c1 = Symbol.for("c");
let c2 = Symbol.for("c");
console.log(c1 === c2);     // true
// 一
let c1 = Symbol.for("c");
let obj = {
    [c1]: 123,    // 这里使用了 c1 变量,c1变量是Symbol
    c1: 123,
    c2: 123
}
console.log(obj);   // {c1: 123, c2: 123, Symbol(c): 123}

// 二
let obj = {}
obj[c1] = 3;
obj.c2 = 4;
console.log(obj);       // {c2: 4, Symbol(c): 3}
console.log(obj[c1]);   // 3

// 三
let b1 = Symbol("b");
let obj = {}
Object.defineProperty(obj, b1, { value: 123 });
console.log(obj);       // {Symbol(b): 123}

// 四
const user = Symbol("zhangsan");
console.log(user);      // Symbol(zhangsan)

解构赋值

介绍

  • 解构赋值可以理解为赋值操作的语法糖,它是针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。代码书写上言简意赅,语义明确,也方便了对象数据的读取操作。

实质

  • ES6中只要某种数据有Iterator接口(也就是可以循环迭代),都可以进行数组的解构赋值。
{
    let a, b, c;
    [a, b, c] = [1, 2];      // [] 类似模板语法
    console.log(a, b, c);   // 1 2 undefined
}   // {} 是块级作用域


{
    let a, b, c;
    [a, b, c = 6] = [1, 2];  // c赋值的话就是赋值后的值,没有赋值就输出默认值6
    console.log(a, b, c);   // 1 2 6
}

{
    let a, other;
    [a, ...other] = [1, 2, 3, 4, 5, 6];     // a = 1, other 解构成了数组
    console.log(a, other);                  // 1  [2, 3, 4, 5, 6]
}

{
    let a, b;
    [a,,,b] = [1,2,3,4];    // , 占位符
    console.log(a,b);       // 1 4
}

{
    let a, b;
    ({ a, b } = { a: 1, b: 2 })     // 解构对象,使用{} ,外面要用 () 括起来
    console.log(a, b);              // 1 2
}

{
    let num, total;
    ({ a: num, b: total } = { a: 3, b: 4 });    // 替换变量名
    console.log(num, total);                    // 3 4
}

解构后端返回给我们的json数据,有可能后端的变量名不是我们想要的

{
    // 假如这个是后端返回的数据
    function fn() {
        return {
            "user_id": 1,
            "nickname": "桑心得难人",
            "mobile": 10086,
            "photo_url": "https://123.jpg",
            "info": {
                "real_name": "张三"
            },
            "orderList": [
                {
                    "order_id": 1
                },
                {
                    "order_id": 2
                }
            ]
        }
    };

    // 前端解构
    let a = fn();
    let { user_id: userId, photo_url: photoUrl, info: { real_name: realName }, orderList: [{ order_id: orderId }] } = a;
    console.log(userId, photoUrl, realName, orderId);   // 1 'https://123.jpg' '张三' 1
}

字符串的扩展方法以及模板字符串

在es6中使用{}来处理超过范围的Unicode字符

{
    const str1 = "\u20bb7";
    console.log(str1);       // ₻7  es5乱码

    const str2 = "\u{20bb7}"; 
    console.log(str2);       // 𠮷   es6正确输出
}

在字符串for循环中,es6使用for-of处理Unicode字符

{
    const str = "\u{20bb7}";
    for (let i = 0; i< str.length; i ++) {
        console.log(str[i]);      // � � es5 乱码
    }

    for (let s of str) {
        console.log(s);           // 𠮷 es6正确输出
    }
}

字符串的一些判断操作

{
    let str = "123zhangsan456";

    // 判断字符串中是否包含指定的字符串
    console.log(str.includes("zhangsan"));          // true

    // 判断字符串是否是以指定的字符串开头
    console.log(str.startsWith("zhangsan"));        // false
    // 判断字符串第几位是否是以指定的字符串开头
    console.log(str.startsWith("zhangsan", 3));     // true
    
    // 判断字符串是否是以指定的字符串结尾
    console.log(str.endsWith("456"));               // true

    // 将字符串重复拼接指定的次数
    str = str.repeat(2);
    console.log(str);                               // 123zhangsan456123zhangsan456
}

{
    // 字符串头部补充指定的字符串
    let str1 = "00";
    str1 = str1.padStart(8, "*");     
    console.log(str1);                              // ******00

    // 字符串尾部补充指定的字符串
    let str2 = "11";
    str2 = str2.padEnd(8, "*");
    console.log(str2);                              // 11******
}

{
    // 拼接字符串
    const username = "zhangsan";
    const age = 18;
    const es5 = "我叫"+username+", 今年"+age;
    const es6 = `我叫${username}, 今年${age}`;
    console.log(es5);
    console.log(es6);
}

数组扩展与运算符扩展

扩展运算符 "..." 三个点

{
    // 复制list数组,到一个新的数组, // 改变原来的数组,新的数组也不会改变
    const list = [1, 2, 3, 4];
    let list2 = [...list];
    list.push(5);               // 改变原来的数组
    console.log(list);          // [1, 2, 3, 4, 5]
    console.log(list2);         // [1, 2, 3, 4]
}

{
    // 分隔数组
    const totalList = [1, "a", "b", "c"];
    let [, ...strList] = totalList;             // , 占位表示丢弃数组的第一个下标
    console.log(strList);                       // ['a', 'b', 'c']
}

{
    function add(x, y) {
        return x + y;
    }

    let addList = [1, 2];
    console.log(add(...addList));       // 3
}

{
    // fill 给数组里面填充数据
    const list = [1, 2, 3, 4, 5];
    let list2 = [...list].fill(3);          // fill 替换数据为3
    console.log(list2);                     // [3, 3, 3, 3, 3]
    let list3 = [...list].fill('a', 1, 3);   // fill("替换成的数据", "替换开始的下标", "替换下标从0开始到指定的替换数")
    console.log(list3);                     // [1, 'a', 'a', 4, 5]
}

{
    // find  findIndex 搜索数组数据
    const list = [{ title: "es6" }, { title: "webpack", id: 2 }, { title: "webpack", id: 3 }];

    let result = list.find(function (item) {
        return item.title === 'webpack';       // 找到第一个符合条件的数据就返回
    });
    console.log(result);    // {title: 'webpack', id: 2}

    let resultIndex = list.findIndex(function (item) {
        return item.title === 'webpack';       // 找到第一个符合条件的数据返回数据所在的下标
    });
    console.log(resultIndex);    // 1   
}

{
    // includes
    const list = [1, 2, 3, 4, 5, 6];
    let result = list.includes(2);      // 判断数组中是否包含指定的数据
    console.log(result);                // true
}

{
    // flat 展开数组操作
    const list = [1, 2, 3, [4, 5, 6, [7, 8, 9]]];
    let flatList = [].concat(...list);  // 多层嵌套需要使用循环
    console.log(flatList);              // [1, 2, 3, 4, 5, 6, Array(3)]

    let flatList2 = list.flat(2);       // 指定展开的层数,不传默认只展开一层
    console.log(flatList2);             // [1, 2, 3, 4, 5, 6, 7, 8, 9]
}
{
    // 复制对象    // 适用于基本类型对象,扩展运算符复制的对象中有对象的话,里面的对象复制的是对象的指针(原对象中的对象改变,复制后的对象也会改变)
    const obj = { name: "zhangsan", age: 18 };
    let newObj = { ...obj };        // ...复制对象, es8的语法,如果报错,需要安装transform-object-rest-spread插件 
    console.log(newObj);            // {name: "zhangsan", age: 18}
}

{
    // 复制对象时,改变对象的值
    const obj1 = { name: "zhangsan", age: 18 };
    let obj2 = { ...obj1, name: "lisi" }
    console.log(obj2);              // {name: 'lisi', age: 18}
}

{
    // 合并对象
    const obj1 = { name: "zhangsan", age: 18 };
    const obj2 = { sex: 1 }
    let obj3 = { ...obj1, ...obj2 };
    console.log(obj3);                  // {name: 'zhangsan', age: 18, sex: 1}
}

复制对象如果报错Unexpected token

需要安装插件transform-object-rest-spread,使用命令

npm i babel-plugin-transform-object-rest-spread

 在.babelrc文件中配置


map、reduce

map

{
    // json数组
    const json = [{ title: "es6", status: 1 }, { title: "es7", status: 0 }, { title: "es8", status: 1 }];
    let map = json.map(function (item) {  // 使用 map
        return { statusTxt: item.status ? "正常" : "禁用" }   // 这种每个变量都要重新定义,不然新对象中没有这个变量
    });
    console.log(json);          // {title: 'es6', status: 1} {title: 'es7', status: 0} {title: 'es8', status: 1}
    console.log(map);           // {statusTxt: '正常'} {statusTxt: '禁用'} {statusTxt: '正常'}
}

{
    // json数组
    const json = [{ title: "es6", status: 1 }, { title: "es7", status: 0 }, { title: "es8", status: 1 }];
    let map = json.map(function (item) {  // 使用 map
        let obj = {};
        Object.assign(obj, item);   // 使用Object.assign方法,把item的数据复制给一个新的对象
        obj.status = item.status ? "正常":"禁用";
        return obj;

    });
    console.log(json);          // {title: 'es6', status: 1} {title: 'es7', status: 0} {title: 'es8', status: 1}
    console.log(map);           // {title: 'es6', status: '正常'} {title: 'es6', status: '禁用'} {title: 'es6', status: '正常'}
}

reduce

{
    /**
     * 统计每个字符在字符串中出现的次数
     * 
     * reduce 对数组中的每个元素进行一次回调,升序执行然后将回调值汇总一个返回值
     * @params fn(acc, currentValue, currentIndex, Array), initalValue
     */
    const letterList = 'aaba3ceseee';
    const result = letterList.split('').reduce(function (acc, cur) {
        acc[cur] ? acc[cur]++ : acc[cur] = 1;
        return acc;
    }, {});
    console.log(result);                 // {3: 1, a: 3, b: 1, c: 1, e: 4, s: 1}
}

{
    //展开多层数组
    const list = [1, ['2nd', 2, 3, ['3rd', 4, 5]], ['2nd', 6, 7]];
    const deepFlat = function (list) {
        return list.reduce(function (acc, cur) {
            return acc.concat(Array.isArray(cur) ? deepFlat(cur) : cur);
        }, []);
    }
    let flatList = deepFlat(list);
    console.log(flatList);               // [1, '2nd', 2, 3, '3rd', 4, 5, '2nd', 6, 7]
}

语法糖

{
    let name = 'zhangsan';
    let age = 18;

    // es5定义json对象的方式
    var es5Obj = {
        name: name,
        age: age,
        sayHello: function () {
            console.log('this is es5Obj')
        }
    }
    // es6简化版的方式
    let es6Obj = {
        name,
        age,
        sayHello() {
            console.log('this is es6Obj')
        }
    }
}

{
    let key = 'name';

    // es5语法
    let es5Obj = {}
    es5Obj[key] = 'zhangsan'
    console.log(es5Obj);        // {name: 'zhangsan'}

    // es6 语法
    let es6Obj = {
        [key]: 'lisi'
    }
    console.log(es6Obj);        // {name: 'lisi'}
}

{
    // Object.is() 判断两个对象是否相等,和'==='类似,唯一的区别是判断NaN的不同
    console.log(Object.is(NaN, NaN));   // true
    console.log(NaN === NaN);           // false
}

{
    // Object.assign() 复制对象,外层对象深拷贝,内层对象浅拷贝:原对象改变后新对象也会改变
    const sourceObj = { name: '小米', age: 18, info: { height: 180 } };
    let targetObj = {};
    Object.assign(targetObj, sourceObj);
    sourceObj.info.height = 160;
    sourceObj.age = 16;
    console.log(targetObj);    // {name: '小米', age: 18, info: { height: 160 }}
}

{
    // Object.keys()    遍历对象的 key
    const json = { name: "zhangsan", age: 18 };
    let obj = {};
    for (const key of Object.keys(json)) {
        console.log(key);           // 1 name 2 age
        obj[key] = json[key];
    }
    console.log(obj);               // {name: 'zhangsan', age: 18}
}

{
    // Object.values()      遍历对象的value
    const json = { name: "zhangsan", age: 18 };
    for (const value of Object.values(json)) {
        console.log(value);         // 1 zhangsan 2 18
    }
}

{
    // Object.entries()     遍历对象的 键和值
    const json = { name: "zhangsan", age: 18 };
    for (const entrie of Object.entries(json)) {
        console.log(entrie);        // 1 ['name', 'zhangsan'] 2 ['age', 18]
    }
}


Map、WeakMap

  • Map
    • JavaScript中的对象,实质就是键值对的集合(Hash结构),但是在对象里却只能用字符串作为键名。在一些特殊的场景里就满足不了我们的需求了,正因为此, Map这一数据提出了,它是JavaScript中的一种更完善Hash结构。
{
    let map1 = new Map();
    map1.set("name", "zhangsan");               // 添加元素
    map1.set("name", "zhangsan").set("age", 18) // 链式

    let map2 = new Map([["name", "zhangsan"], ["age", 18]]);    // 在创建对象时初始化元素

    console.log(map2.size);         // 查询map中有多少键值对 

    console.log(map2.get("name"));  // 读取key的value

    console.log(map2.has("sex"));   // 判断map中是否存在指定的key, true:存在,false:不存在

    map2.delete("age");             // 删除map中指定的元素

    map2.clear();                   // 移除map中的所有元素
}

{
    // 遍历map
    let map = new Map([
        ["name", "zhangsan"],
        ["age", 18]
    ]);
    for (const key of map.keys()) {         // keys() 遍历map的 key
        console.log(key);
    }
    for (const value of map.values()) {     // values() 遍历map的value
        console.log(value);
    }
    for (const entrie of map.entries()) {   // entries() 遍历map的键值,可以省略entries()
        console.log(entrie);
    }
}
  • WeakMap
  1. 是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。

  2. 因为键名所指的对象不触发垃圾回收机制,当document(使用场景一般是绑定document)不存在时这个map也就销毁了

  3. 没有clear, 没有size, 无法遍历(因为是弱引用)

<!-- html代码 -->
<div id="test"></div>    // 当 test 存在的时候,下面的js代码正常执行。当 test 不存在的时候,下面的js代码报错 TypeError: Invalid value used as weak map key,因为 test的document对象已经不存在了

// js代码
{
    let ulObj = document.getElementById('test')
    let weakmap = new WeakMap([
        [ulObj, 10]
    ])
    console.log(weakmap)
}

Set、WeakSet

  • set
    • 是ES6给开发者提供的一种类似数组的数据结构,可以理解为值的集合。可以按照插入的顺序迭代它的元素。它和数组的最大的区别就在于: 它的值不会有重复项。
{
    let set1 = new Set();
    set1.add(1);                            // 添加元素
    set1.add(1).add(2)                      // 链式

    let set2 = new Set(['a', 'b', 'c']);    // 在创建对象时初始化元素

    console.log(set2.size);                 // 查询set中有多少个元素

    console.log(set2.has('a'));             // 判断set中是否存在指定的元素, true:存在,false:不存在

    set2.delete('a');                       // 删除set中指定的元素

    set2.clear();                           // 移除set内的所有元素
}
{
    let set = new Set(['a', 'b', { 'username': 'zhangsan' }]);

    for (const item of set) {               // 遍历set中的元素, 同 values()、keys()相等
        console.log(item);                  // 按插入顺序输出
    }

    for (const entrie of set.entries()) {
        console.log(entrie);                // 输出 键值相同的数据
    }
}
  • WeakSet
  1. 数组成员必须是对象
  2. WeakSet结构也提供了add()方法,delete()方法,has()方法给开发者使用,作用与用法跟Set结构完全一致。
  3. WeakSet结构不可遍历。因为它的成员都是对象的弱引用,随时被回收机制回收,成员消失。所以WeakSet 结构不会有keys(),values(),entries(),forEach()等方法和size属性。


Map、Set、Array、Object 的区别

{
    let array = []
    let obj = {}
    let map = new Map()
    let set = new Set()

    const user = { username: 'zhangsan' }

    // 增加
    array.push(user)
    obj['username'] = 'zhangsan'
    map.set('username', 'zhangsan')
    set.add(user)
    console.log('add', array, obj, map, set)

    // 查询判断是否存在
    const resultArray = array.includes(user)
    const resultObj = 'username' in obj
    const resultMap = map.has('username')
    const resultSet = set.has(user)
    console.log('search', resultArray, resultObj, resultMap, resultSet)

    // 修改
    array.forEach(function (item) {
        item.username = item.username ? 'lisi' : ''
    })
    obj['username'] = 'lisi'
    map.set('username', 'lisi')
    set.forEach(function (item) {
        item.username = item.username ? 'lisi' : ''
    })
    console.log('update', array, obj, map, set)

    // 删除
    const index = array.findIndex(function (item) {
        return item.username
    })
    array.splice(index, 1)
    delete obj.username
    map.delete('username')
    set.delete(user)
    console.log('delete', array, obj, map, set)
}
{
    // 类型转换

    // 定义一个对象
    let obj = { 'username': 'zhangsan', 'age': 18 };

    // 对象转map
    let map = new Map(Object.entries(obj));

    // map转对象
    let obj2 = Object.fromEntries(map);

    // 数组转set
    let array = [1, 2, 3];
    let set = new Set(array);

    // set转数组
    let array2 = Array.from(set);
}

代理Proxy、反射Reflect

Proxy

{
    // 定义一个对象,例如是后端返回给我们的对象
    let user = { id: 1, username: "admin", age: 18, createTime: 2021, _password: '123456' };

    // 代理对象
    let userProxy = new Proxy(user, {

        /**
         * 读取操作
         * @param {目标对象} target 
         * @param {目标对象的键} key 
         */
        get: function (target, key) {
            switch (key) {
                case "username":
                    return target[key] + "_123";    // 读取,如果是指定是字段‘username’ 就在后面加上指定的字符
                default:
                    return target[key];

            }
        },


        /**
         * 设置操作
         * @param {目标对象} target 
         * @param {目标对象的键} key 
         * @param {目标对象的值} value 
         */
        set: function (target, key, value) {
            if (Object.is(key, "id")) {         // 设置,如果是指定的字段,就跳过设置,原样输出
                return target[key];             // 原样输出
            } else {
                return target[key] = value;     //  输出设置后的值
            }
        },

        /**
         * 判断 in 操作
         * 判断对象中的属性是否存在
         * 
         * @param {目标对象} target 
         * @param {目标对象的键} key 
         * @returns true:存在,false:不存在
         */
        has: function (target, key) {
            if (key in target) {
                console.log(`${key}:`, target[key])
                return true
            } else {
                console.log('并无此属性')
                return false
            }
        },

        /**
         * 删除操作
         * @param {目标对象} target 
         * @param {目标对象的键} key 
         * @returns true:删除成功,false:删除失败
         */
        deleteProperty: function (target, key) {
            if (key.indexOf('_') === 0) {
                console.warn('私有属性不能被删除')
                return false
            } else {
                delete target[key]
                return true
            }
        },

        /**
         * 拦截Object.keys()
         * @param {目标对象} target 
         * @returns 
         */
        ownKeys(target) {
            return Object.keys(target).filter(function (item) { // 遍历 对象target中的所有键
                return item !== 'id' && item.indexOf('_') !== 0 // 过滤掉id和_开头的属性
            })
        }
    });

    // 调用读取
    console.log(userProxy.username);            // admin_123 通过[对象.变量]的方式读取,不要通过[对象]的方式
    // 设置调用
    userProxy.id = 2;
    userProxy.age = 30
    console.log(userProxy.id, userProxy.age);   // 1 30    
    // 判断 in 操作
    console.log('username' in userProxy);       // true     // 判断 userProxy 对象中是否存在属性 username
    // 删除操作
    console.log(delete userProxy['_password']); // false    // Uncaught TypeError: 'deleteProperty' on proxy 这个错误在es5会出现,es6中不会出现
    // 拦截Object.keys()操作
    console.log(Object.keys(userProxy));        // ['username', 'age', 'createTime']
}

 Reflect

{
    // Reflect  所有Object的方法和Proxy的方法,Reflect中都有,
    // 编程的过程中推荐使用 Reflect函数式编程的方式 操作对象
    
    // 创建一个对象
    let user = { id: 1, username: "admin", age: 18, createTime: 2021 };

    // get
    console.log(Reflect.get(user, 'username'));     // admin    // 字段需要使用 引号'' 引起来
    // set
    Reflect.set(user, 'username', 'zhangsan');
    console.log(user.username);
    // 判断 has
    console.log(Reflect.has(user, 'username'));     // true     // Object中的语法 'username' in user
}

使用Proxy和Reflect实现双向数据绑定,

ps:请教,我没看懂txtObje 和 objProxy是怎么代理起来的,它们好像没有关系?有看懂的同学评论区解答一下,谢了

<!-- html -->
<body>
    <input type="text" id="input">    // input输入内容后, i 中的内容会跟着改变
    <h2>输入的值:<i id="txt"></i></h2>    // i 的值改变后,input的内容也会改变
    <script src="src/js.js"></script>
</body>


// js 
{
    // 获取dom元素
    const inputObj = document.getElementById("input");
    const txtObj = document.getElementById("txt");

    // 初始化对象
    const obj = {};

    // 代理选项
    const handler = {
        get: function (target, key) {
            return Reflect.get(target, key);
        },
        set: function (target, key, value) {
            if (key === 'text') {
                inputObj.value = inputObj.value === value ? inputObj.value : value;
                txtObj.innerHTML = value;
            }
            return Reflect.set(target, key, value);
        }
    }

    // 代理对象
    const objProxy = new Proxy(obj, handler);

    // 给 input元素 添加键盘键入事件
    inputObj.addEventListener('keyup', function(e) {
        objProxy.text = e.target.value;
    });
}

函数参数默认值

{
    function es6(x, y = 'world') {
        console.log('es6', x + y);
    }
    es6('hello', '');   // es6 hello
}

函数参数可变长度

{
    // 使用扩展运算符 ...
    function add(...rest) {
        let sum = 0;
        for (const value of rest) {
            sum += value;
        }
        console.log(sum);
    }
    add(1, 2, 3, 4, 5);
}

函数尾调用(替换递归)

{
    function step2 (x) {
        console.log('尾调用', x);
    }

    // 一个函数的返回结果是另一个函数
    function step1 (x) {
        return step2(x);
    }

    step1(1);
}

箭头函数

{
    function arrow (x) {
        console.log('es5 函数 ', x);
    }
    arrow('5');
}

{
    // 声明函数,箭头函数是匿名函数
    const arrow = (x) => {
        console.log('es6 箭头函数 ', x);
    }
    arrow(6);  // 调用
}

箭头函数,只有一个参数的时候,参数的 () 可以省略。函数体 只有一行代码的时候 {} 可以省略

{
    // 未省略
    const arrow = (x) => {
        return x * 2;
    }
    const result = arrow(6);  // 调用
    console.log(result);

    // 省略
    const arrow2 = x => x * 2;
    console.log(arrow2(6));
}

箭头函数不绑定 this,或者绑定的对象不会变换

setTimeout 是 windows的函数,会改变this的指向,所以没得指 /摊手

使用箭头函数,this 依旧指向对象本身


es5的方式

{
    // es5 的方式

    // 类构造函数
    function Person(name, age) {
        this.name = name;
        this.age = age;
    }
    // 类中的方法
    Person.prototype.sayHello = function () {
        console.log("hello,我是 es5 方式的类中的方法");
    }

    const p = new Person('小明', 18);
    console.log(p);
    p.sayHello();
}

es6的方式

引入了新的关键字class

{
    // es6 的方式,引入了新的关键字 class
    class Person {
        // 构造函数
        constructor(name, age) {
            this.name = name    // 不用加逗号,
            this.age = age
        }   // 不要加逗号,

        // 类中的方法
        sayHello () {
            console.log("hello,我是 es6 方式的类中的方法");
        }
    }

    const p = new Person('小花', 17);
    console.log(p);
    p.sayHello();
}

类的 get set

{
    // 引入关键字 get set
    // 类中的 get set 属性,注意是属性,不是方法
    class Person {
        constructor(name = '小红') {
            this.name = name
        }
        get attrName () {   // get set 的属性名称自定义,但是不能和要修改的属性名称相同
            return this.name;
        }
        set attrName(name) {
            this.name = name
        }
    }

    const p = new Person();
    console.log('get set', p.attrName);     // 小红
    p.attrName = '小明' 
    console.log('get set', p.attrName);     // 小明
}

类的静态方法

{
    // 类的静态方法
    class Person {
        constructor(name = '小红') {
            this.name = name
        }
        static sayHello () {
            console.log('hello, 我是 es6 的静态方法');
        }
    }

    // 静态方法,可以  类名.方法名  进行调用,不需要使用new 
    Person.sayHello();
}

类的静态属性

es6的方式

{
    // 类
    class Person {
        constructor(name = '小红') {
            this.name = name
        }
    }

    // 静态属性
    Person.age = 18;

    console.log(Person.age);
}

es7的方式

需要使用插件

npm i babel-preset-es2017
{
    // 类
    class Person {
        constructor(name = '小红') {
            this.name = name
        }
        static age = 18
    }

    console.log(Person.age);
}

类的继承

引入了关键字 extends 

{
    // 父类
    class Person {
        constructor(name = '小红') {
            this.name = name
        }
    }

    // 子类
    class Child extends Person {
    }

    console.log(new Child());   //{name: '小红'}
}
{
    // 父类
    class Person {
        constructor(name = '小红') {
            this.name = name
        }
    }
    console.log(new Person());  // {name: '小红'}

    // 子类
    class Child extends Person {
        constructor(name, age) {
            super(name)         // 子类的构造方法中必须调用父类的构造
            this.age = age
        }
    }

    console.log(new Child('小黄', 16)); // {name: '小黄', age: 16}
}

遍历对象

{
    // 应用场景
    const obj = {
        color: 'red',
        price: 18,
        size: 'small',
        [Symbol.iterator]() {
            let index = 0
            const values = Object.values(this)
            return {
                next() {
                    if (index < values.length) {
                        return {
                            value: values[index++],
                            done: false
                        }
                    } else {
                        return {
                            done: true
                        }
                    }
                }
            }
        }
    }

    for (const value of obj) {
        console.log(value)  // red 18 small
    }
}

js 模块化( import 、export )

A 写了一些 js 代码,B 想调用 A 的代码
A 的 js 代码 首先要 export 导出
B 要 import 导入 A 的代码

方式一

export

// a.js 文件

// 导出一个变量
export let a = 3;      

// 导出 一个方法
export function sayHello() {
    console.log('我是方法');
}

// 导出一个类
export class Test {
    sayTest () {
        console.log('我是类方法');
    }
}

import

<script src="src/b.js" type="module"></script> <!-- 使用模块化,需要加 type="module" -->
// b.js 文件

import { a, sayHello , Test} from "./a.js";

console.log(a);

sayHello();

const test = new Test();
test.sayTest();

使用 * 星号通配符,引入所有模块

import * as test from "./a.js";

console.log(test.a)

方式二

export default

// a.js 文件

let a = 3;

function sayHello() {
    console.log('我是方法');
}

class Test {
    sayTest() {
        console.log('我是类方法');
    }
}

// 导出
export default {
    a,          // 导出 变量
    sayHello,   // 导出 方法
    // Test     // 类不导出
}

import

// b.js 文件

import test from "./a.js";

console.log(test.a)

test.sayHello();

异步调用

Promise

示例一

{
    // es6 提供了 Promise 异步调用对象
    // Promise 对象构造函数接收两个方法:
    // 第一个方法:resolve 解决(成功方法)
    // 第二个方法:reject 拒绝(失败方法)
    const p = new Promise((resolve, reject) => {
        // ... 代码省略,先执行这里的代码
        resolve();  // 然后再执行 then 里面的代码
        // reject();
    });

    // Promise 对象提供了 then() 回调函数,用于接收 resolve、reject 参数
    p.then(() => {
        console.log("解决了");
    }, () => {
        console.log("拒绝了");
    })
}

模糊知识点:调用 then 方法后,会先执行 Promise对象里面的代码,执行完成后,才会执行对应的resolve、reject函数

示例二

{
    // 创建 Promise
    const fun = (num) => {
        return new Promise((resolve, reject) => {
            if (num == 200) {
                // 成功了
                resolve();
            } else {
                // 失败了
                const err = new Error('我是异常信息');
                reject(err);
            }
        });
    }

    // 调用
    fun(500).then(() => {
        console.log("200 成功了");
    }, e => {
        console.log('打印异常信息', e.message);
        console.log("500 失败了");
    });

    // 或者可以使用 catch 捕获异常
    /* fun(500).then(() => {
        console.log("200 成功了");
    }).catch(e => {
        console.log('打印异常信息', e.message);
        console.log("500 失败了");
    }); */
}

实例三:请求真实接口

// 封装一个 post 请求方法
// 参数 url:请求地址,例如:http://localhost:8080/test
// 参数 data:请求数据,例如{name:"张三"}
let post = (url, data) => {
    return new Promise((resolve, reject) => {
        axios({
            method: 'post',
            url: url,
            data: data,
        }).then((response) => {
            // 接口请求成功
            resolve(response.data);
        }).catch((error) => {
            // 接口请求失败
            reject(error);
        })
    });
}

----------------------------------------------华丽分割线------------------------------------------------
// 调用发起 post 接口请求
let url = "http://localhost:8080/test"
let data ={name:"zhangsan"}

post(url, data).then((res) => {
    // 打印返回结果
    console.log("res = ", res);
}, (e) => {
    // 打印失败信息
    console.log(e);
})

Promise.all(数组).then() : 接收多个Promise对象,当所有的Promise对象执行成功后,继续向下执行all的then,参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例,不是的话会转为Promise实例

Promise.race(数组).then() :接收一组Promise对象,当只有一个Promise对象执行成功后,就可继续向下执行race的then


Generator

使用 Generator 需要安装组件,安装命令如下

npm install --save babel-polyfill

学习 Generator 之前要先了解下 iterator 迭代,iterator 是一种接口,目的是为了给不同的数据结构提供统一的循环方式,任何数据结构如果部署了Iterator接口,就能够实现遍历的操作。Array、String、Set、Map、函数的argument对象 具备原生的Iterator接口,可以进行变量。Object如果需要遍历的话,需要自己实现

// iterator 示例代码
{
    const arr = [1,2,3];
    const obj = arr[Symbol.iterator]();
    
    console.log(obj.next());         // {value: 1, done: false}
    console.log(obj.next());         // {value: 2, done: false}
    console.log(obj.next());         // {value: 3, done: false}
    console.log(obj.next());         // {value: undefined, done: true}
}

开始学习 Generator

{
    // 创建一个 Generator 函数
    // Generator 的 语法标志 是 function* 
    // 函数里面 通过关键字 yield 赋值
    const say = function* () {
        yield 'a'
        yield 'b'
        yield 'c'
    }

    // 创建一个 Generator 对象
    const obj = say();

    // 输出 , 这个结果 和 iterator 结构一致
    console.log(obj.next());    // {value: 'a', done: false}
}

Generator 应用场景一:遍历对象

{
    // 有一个对象
    let obj = {
        a:1,
        b:2,
        c:3
    }

    // 给对象中添加一个 Generator 方法,这样这个对象就具有了 iterator 迭代功能
    obj[Symbol.iterator] = function * () {
        for (const key of Object.keys(obj)) {
            yield obj[key]
        }
    }

    // 遍历对象结果
    for (const value of obj) {
        console.log(value); // 1  2  3
    }
}

Generator 应用场景二:长轮询

{
    // 长轮询
    function fn1() {
        return new Promise(resolve => {
            setTimeout( () => {
                console.log('查询中')
                resolve({code: 0})
            },1000)
        })
    }

    // 创建 Generator 方法
    const getStatus = function* () {
        yield fn1()
    }

    function autoGetStatus() {
        const gen = getStatus() // 创建 Generator 实例
        const status = gen.next()   // 调用 Generator 方法,得到 结果
        status.value.then(res => {
            if(res.code === 0) {
                console.log('用户付款成功')
            } else {
                console.log('暂未付款')
                setTimeout( () => autoGetStatus(), 500)
            }
        })
    }

    autoGetStatus()
}

Generator 应用场景三:异步同步化

需求:

{
    // 需求,我想让 1 先进行打印,然后再打印 2 , 但是实际执行结果是 2 1
    function test() {
        setTimeout(()=>{
            console.log("1");
        }, 100);
        console.log("2");
    }
    test();
}

实现:

{
    const ajax = function* () {
        console.log('1')
        yield function (cb) {
            setTimeout(() => {
                console.log('2')
                cb && cb()
            }, 1000)
        }
        console.log('3')
    }

    const runAjax = ajax()
    const first = runAjax.next()        // 1
    first.value(() => runAjax.next())   // 2 3
}

async await

async

async作用是声明一个异步函数,表示这个函数是异步函数。

被async定义的函数会默认返回一个Promise对象resolve的值。因此对async函数可以直接then,返回值就是then方法传入的函数。

{
    async function fn1() {
        console.log(1);
        return 1;
    }
    fn1().then(res =>{
        console.log(res);
    })
}

await

await 也是一个修饰符,只能放在async定义的函数内。可以理解为等待。
await 修饰的如果是Promise对象:可以获取Promise中返回的内容(resolve或reject的参数),且取到值后语句才会往下执行;
如果不是Promise对象:把这个非promise的东西当做await表达式的结果。

需求:有如下代码,我想 1 执行完成后,再执行 2 , 2 执行完成后,再执行 3。但是实际结果是 123 同时执行

{
    function fn1() {
        setTimeout(() => {
            console.log('任务1')
        },1000)
    }

    function fn2() {
        setTimeout(() => {
            console.log('任务2')
        },1000)
    }

    function fn3() {
        setTimeout(() => {
            console.log('任务3')
        },1000)
    }

    function init() {
        fn1()
        fn2()
        fn3()
    }

    init()
}

使用 async await 实现 异步函数达到同步的效果

{
    function fn1() {
        return new Promise(resolve => {
            setTimeout(() => {
                console.log('1')
                resolve()   // 这里调用 resolve 或者 reject,await 才会向下继续执行
            },1000)
        })
    } 
    
    function fn2() {
        return new Promise(resolve => {
            setTimeout(() => {
                console.log('2')
                resolve()
            },1000)
        })
    }   

    function fn3() {
        return new Promise(resolve => {
            setTimeout(() => {
                console.log('3')
                resolve()
            },1000)
        })
    }  
    
    async function init(fn1,fn2,fn3) {
        await fn1()
        await fn2()
        await fn3()
    }
    init(fn1, fn2, fn3)
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

7 号

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值