ES6 学习笔记 ——(一)

333 阅读7分钟

笔记内容为 尚硅谷Web前端ES6教程,涵盖ES6-ES11 教学课程笔记整理、

let 特性

---varlet备注
是否能重复声明可以重复声明不能重复声明let star = a; let star = b; 是错误的
作用域全局、函数、eval多了一个块级作用域eval 是 ES5 在严格模式下的作用域
是否存在变量提升let 只能先定义后使用
是否影响作用域链外面声明的 let 变量,在函数里面可以正常读取

箭头函数

  1. this 是静态的,this 始终指向函数声明时所在作用域下的 this 的值
  2. 不能作为构造器实例化对象
// 以下用法是错误的
let Person = (name, age) => {
    this.name = name;
    this.age = age;
}
let me = new Person('xiao', 30);
console.log(me);
  1. 不能使用 arguments 变量
// 以下用法是错误的
let fn = () => {
    console.log(arguments);
}
fn(1,2,3);
  1. 箭头函数的简写
    1. 省略小括号,当形参有且只有一个的时候
        let add = n => {
            return n + n;
        }
        console.log(add(9));
    
    1. 省略花括号,当代码体只有一条语句的时候,此时 return 必须省略,而且语句的执行结果就是函数的返回值
        let pow = n => n*n;
        console.log(pow(9));
    

rest 参数

  • ES6 引入 rest 参数,用于获取函数的多余参数,用来代替 arguments
  • rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中
  • arguments 对象不是数组,而是一个类似数组的对象,所以为了使用数组的方法,必须使用 Array.from 先将其转为数组
  • rest 参数就是一个真正的数组,数组特有的方法都可以使用
  • rest 参数必须要放到参数最后

ES5 获取实参的方式

function date() {
    console.log(arguments);
}
date('a', 'b', 'c');

ES6 获取参数的方法

function date(...args) {
    console.log(args);
}

扩展运算符

  • 扩展运算符 ... 能够将 数组 转换为逗号分隔的 参数序列

1. 数组的合并

const arr1 = ['a', 'b'];
const arr2 = ['c', 'd'];
const arr3 = [...arr1, ...arr2]; // ['a', 'b', 'c', 'd']

2. 数组的克隆

  • 注意,这里的是浅拷贝
const arr1 = ['a', 'b'];
const arr2 = [...arr1]; // ['a', 'b']

3. 将伪数组转为真正的数组

const divs = document.querySelectorAll('div');
const divArr = [...divs]; // [<div>, <div>, <div>]
  • 对于 arguments 也可以使用扩展运算符将其转换为真正的数组,因为 arguments 对象不是数组,是一个类似数组的对象

Symbol

  • ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值
  • 它是 JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型
  • 七种数据类型的简记方式
    • USONB (you are so niubility)
    • u undefined
    • s stringsymbol
    • o object
    • n nullnumber
    • b boolean

Symbol 特点

  1. Symbol 的值是唯一的,用来解决命名冲突的问题
  2. Symbol 值不能与其他数据进行运算
  3. Symbol 定义的对象属性不能使用 for...in 循环遍历,但是可以使用 Reflect.ownKeys 来获取对象的所有键名

创建 Symbol

// 方法一
let s1 = Symbol();
console.log(s1, typeof s1); // Symbol() "symbol"

// 方法二:添加标识的 Symbol
let s2 = Symbol.for('创建');
console.log(s2, typeof s2); // Symbol(创建) "symbol"

用作对象属性名

let youxi = {
    name: "wolf killer",
    [Symbol('say')]: function() {
        console.log("I can say");
    },
    [Symbol('zibao')]: function() {
        console.log('I can zibao');
    }
}

迭代器

  • 迭代器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制
  • 任何数据结构只要部署 Iterator 接口,就可以完成遍历操作
  • 比如,数组、对象,是不同的数据结构,但是都可以进行遍历
  • ES6 创造了一种新的遍历命令 for...of 循环,Iterator 接口主要供 for...of 消费
const xiyou = ['唐僧', '孙悟空', '猪八戒', '沙僧']

for(let v of xiyou) {
    console.log(v);
}
// 唐僧
// 孙悟空
// 猪八戒
// 沙僧

for(let v in xiyou) {
    console.log(v);
}
// 0
// 1
// 2
// 3
  • for...in 循环,前面的变量保存的是键名,因此输出的是 0,1,2,3
  • for...of 循环,前面的变量保存的是键值,因此输出的是数组内容唐僧等
  • 原生具备 iterator 接口的数据(可用 for...of 遍历)
    1. Array
    2. Arguments
    3. Set
    4. Map
    5. String
    6. TypedArray
    7. NodeList

工作原理

  1. 创建一个指针对象,指向当前数据结构的起始位置
const xiyou = ['唐僧', '孙悟空', '猪八戒', '沙僧']
console.log(xiyou); // xiyou 的对象原型 __proto__ 上有一个属性 Symbol.iterator

图片.png

// 使用 Symbol.iterator 这个属性创建一个指针对象
let iterator = xiyou[Symbol.iterator]();
console.log(iterator); // 它的身上有 next 方法

图片.png

  1. 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
let iterator = xiyou[Symbol.iterator]();
// 调用对象的 next 方法,指向第一个成员
console.log(iterator.next());

图片.png

  1. 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
let iterator = xiyou[Symbol.iterator]();
// 不断调用对象的 next 方法,指针后移,返回中包含 value 和 done 属性
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

图片.png 4. 每调用 next 方法返回一个包含 valuedone 属性的对象

自定义遍历数据

需要自定义遍历数据的时候,要想到迭代器

// 声明一个对象
const banji = {
    name: "终极一班",
    stus: [
        'xiaoming',
        'xiaoning',
        'xiaotian',
        'knight'
    ],
    [Symbol.iterator]() {
        // 索引变量
        let index = 0;
        let _this = this;
        return {
            // next 方法
            next: function() {
                if (index < _this.stus.length) {
                    // next 方法返回中包含 value 和 done 属性
                    const result = { value: _this.stus[i], done: false};
                    // 下标自增
                    index++;
                    // 返回结果
                    return result;
                } else {
                    return { value: undefined, done: true};
                }
            }
        }
    }
}

// 遍历这个对象
for (let v of banji) {
    console.log(v);
}
// xiaoming
// xiaoning
// xiaotian
// knight

生成器

声明与调用

  • 生成器函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同
  • 生成器其实就是一个特殊的函数
  • 声明方式有些特殊,需要在 function 关键字和方法名中间加一个星号
// 星号位置三种均可
function * gen(){}
function* gen(){}
function *gen(){}
  • 生成器的调用也很特殊,直接调用,返回的是一个迭代器对象(含有 next 方法)
function * gen() {
    console.log("hello generator");
}
let iterator = gen();
console.log(iterator); // 直接调用并不会输出 hello generator

图片.png

  • 借助迭代器对象中的 next 方法,可以使生成器中的代码执行
function * gen() {
    console.log("hello generator");
}
let iterator = gen();
iterator.next(); // 输出 hello generator
  • yield:函数代码的分隔符,下面的代码被三个 yield 分割成四部分,同样是通过 next 方法来执行
function * gen() {
    yield '一只没有耳朵';
    yield '一只没有尾巴';
    yield '真奇怪';
}

图片.png

function * gen() {
    console.log(111);
    yield '一只没有耳朵';
    console.log(222);
    yield '一只没有尾巴';
    console.log(333);
    yield '真奇怪';
    console.log(444);
}

let iterator = gen();
iterator.next();
iterator.next();
iterator.next();
iterator.next();

// 111
// 222
// 333
// 444
  • 既然使迭代器对象,那么就可以使用 for...of 来进行遍历,每一次的返回结果就是 yield 后面的表达式
function * gen() {
    yield '一只没有耳朵';
    yield '一只没有尾巴';
    yield '真奇怪';
}

// 遍历
for (let v of gen()) {
    console.log(v);
}

// 一只没有耳朵
// 一只没有尾巴
// 真奇怪
function * gen() {
    yield '一只没有耳朵';
    yield '一只没有尾巴';
    yield '真奇怪';
}

let iterator = gen();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

图片.png

生成器函数的参数传递

图片.png

  • 第一个 console.log(iterator.next()); 调用,就会执行生成器函数的第一段,也就是 function * gen(){yield 111; 中间的代码,并且把第一个 yield 后的语句作为返回

图片.png

  • 使用生成器时传入的参数,可以在调用迭代器对象的 next 方法时进行输出

图片.png

  • next 方法可以传入实参
  • 第二个 next 传入的参数,作为第一个 yield 语句整体的返回结果
  • 第三个 next 传入的参数,作为第二个 yield 语句整体的返回结果

生成器函数的实例

实例一

  • 要求:1s 后控制台输出 111,2s 后输出 222,3s 后输出 333
  • 直接使用 setTimeout 进行嵌套编程也可以实现,但是当嵌套层级过多,会出现非常多的缩进,造成 回调地狱 图片.png
  • 使用生成器函数,实现异步编程
    1. 定义三个函数 one,two,three,实现三个延时输出操作
    2. 定义一个生成器函数,使用 yield 语句
    3. 通过调用生成器函数,生成一个迭代器对象 iterator
    4. 调用迭代器对象的 next 方法

图片.png

实例二

  • 要求:模拟异步获取用户数据,订单数据,商品数据
  • 注意:订单数据和商品数据,是以来用户数据的
  • 利用生成器函数 next 方法调用时可以传参的特性,可以把上一步执行的结果,传给下一步 图片.png