笔记内容为 尚硅谷Web前端ES6教程,涵盖ES6-ES11 教学课程笔记整理、
let 特性
| --- | var | let | 备注 |
|---|---|---|---|
| 是否能重复声明 | 可以重复声明 | 不能重复声明 | let star = a; let star = b; 是错误的 |
| 作用域 | 全局、函数、eval | 多了一个块级作用域 | eval 是 ES5 在严格模式下的作用域 |
| 是否存在变量提升 | 是 | 否 | let 只能先定义后使用 |
| 是否影响作用域链 | 否 | 否 | 外面声明的 let 变量,在函数里面可以正常读取 |
箭头函数
- this 是静态的,this 始终指向函数声明时所在作用域下的 this 的值
- 不能作为构造器实例化对象
// 以下用法是错误的
let Person = (name, age) => {
this.name = name;
this.age = age;
}
let me = new Person('xiao', 30);
console.log(me);
- 不能使用
arguments变量
// 以下用法是错误的
let fn = () => {
console.log(arguments);
}
fn(1,2,3);
- 箭头函数的简写
- 省略小括号,当形参有且只有一个的时候
let add = n => { return n + n; } console.log(add(9));- 省略花括号,当代码体只有一条语句的时候,此时 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
string、symbol - o
object - n
null、number - b
boolean
Symbol 特点
- Symbol 的值是唯一的,用来解决命名冲突的问题
- Symbol 值不能与其他数据进行运算
- 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,3for...of循环,前面的变量保存的是键值,因此输出的是数组内容唐僧等- 原生具备 iterator 接口的数据(可用 for...of 遍历)
- Array
- Arguments
- Set
- Map
- String
- TypedArray
- NodeList
工作原理
- 创建一个指针对象,指向当前数据结构的起始位置
const xiyou = ['唐僧', '孙悟空', '猪八戒', '沙僧']
console.log(xiyou); // xiyou 的对象原型 __proto__ 上有一个属性 Symbol.iterator
// 使用 Symbol.iterator 这个属性创建一个指针对象
let iterator = xiyou[Symbol.iterator]();
console.log(iterator); // 它的身上有 next 方法
- 第一次调用对象的
next方法,指针自动指向数据结构的第一个成员
let iterator = xiyou[Symbol.iterator]();
// 调用对象的 next 方法,指向第一个成员
console.log(iterator.next());
- 接下来不断调用 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());
4. 每调用 next 方法返回一个包含
value 和 done 属性的对象
自定义遍历数据
需要自定义遍历数据的时候,要想到迭代器
// 声明一个对象
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
- 借助迭代器对象中的 next 方法,可以使生成器中的代码执行
function * gen() {
console.log("hello generator");
}
let iterator = gen();
iterator.next(); // 输出 hello generator
- yield:函数代码的分隔符,下面的代码被三个 yield 分割成四部分,同样是通过 next 方法来执行
function * gen() {
yield '一只没有耳朵';
yield '一只没有尾巴';
yield '真奇怪';
}
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());
生成器函数的参数传递
- 第一个
console.log(iterator.next());调用,就会执行生成器函数的第一段,也就是function * gen(){到yield 111;中间的代码,并且把第一个 yield 后的语句作为返回
- 使用生成器时传入的参数,可以在调用迭代器对象的 next 方法时进行输出
- next 方法可以传入实参
- 第二个 next 传入的参数,作为第一个 yield 语句整体的返回结果
- 第三个 next 传入的参数,作为第二个 yield 语句整体的返回结果
生成器函数的实例
实例一
- 要求:1s 后控制台输出 111,2s 后输出 222,3s 后输出 333
- 直接使用 setTimeout 进行嵌套编程也可以实现,但是当嵌套层级过多,会出现非常多的缩进,造成
回调地狱 - 使用生成器函数,实现异步编程
- 定义三个函数 one,two,three,实现三个延时输出操作
- 定义一个生成器函数,使用 yield 语句
- 通过调用生成器函数,生成一个迭代器对象 iterator
- 调用迭代器对象的 next 方法
实例二
- 要求:模拟异步获取用户数据,订单数据,商品数据
- 注意:订单数据和商品数据,是以来用户数据的
- 利用生成器函数 next 方法调用时可以传参的特性,可以把上一步执行的结果,传给下一步