目录
11.1 原生具备 Iterator 接口的数据(可用 for...of 遍历)
12.2.2 案例练习 - 模拟获取 用户数据、订单数据、商品数据
一、ECMAScript
1.1 什么是ECMAScript
ECMAScript是由Ecma国际通过ECMA-262标准话的脚本程序设计语言
1.2 什么是ECMA-262
Ecma国际制定了许多标准,而ECMA-262只是其中的一个
1.3 ES6兼容性
ECMAScript 6 compatibility table (kangax.github.io)http://kangax.github.io/compat-table/es6/
二、ES6关键词
2.1 let变量声明以及声明特性
2.1.1 let关键字
let关键字用来声明变量
2.1.2 let关键字特性
- 变量不能重复声明
- 块级作用域(指变量只在代码块内有效)
- 不存在变量提升(变量提升:即在变量创建之前使用,比如var声明的变量,在声明变量前输出这个变量,可输出得到默认值undefined;let则会报错)
- 不影响作用域链(上级代码块中的局部变量下级可用)
2.1.3 let关键字 - 案例练习
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>let关键词案例</title>
<style>
.container{
width: 600px;
}
.page-header{
border-bottom: 1px solid #ccc;
}
.item{
float: left;
width: 100px;
height: 50px;
border: 1px solid red;
margin-left: 20px;
}
</style>
</head>
<body>
<div class="container">
<h2 class="page-header">点击切换颜色</h2>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
<script>
let items = document.getElementsByClassName('item');
// 注意如果使用var 声明变量i,i是全局变量,经过循环后i=3,items[3]找不到对应的元素,将会报错
// 使用let声明变量i, let声明的是局部变量,每一个都保持原来的值
for(let i = 0; i < items.length ;i++){
items[i].onclick = function(){
items[i].style.background = 'skyblue';
}
}
</script>
</body>
</html>
使用var 声明变量 i 的正确写法
<script>
let items = document.getElementsByClassName('item');
for(var i = 0; i < items.length ;i++){
items[i].onclick = function(){
this.style.background = 'skyblue';
}
}
</script>
2.2 const声明常量以及特点
2.2.1 const关键字
const关键字用来声明常量
- 常量:值不能修改的量
2.2.2 const声明的特点
- 一定要赋初始值
- 一般常量使用大写(潜规则)
- 常量值不能修改
- 块级作用域
- 对于数组和对象的元素修改,不算作对常量的修改,不会报错(因为常量所指向的地址未发生改变,数组和对象用const声明 是一种比较稳妥的做法)
三. 变量的解构赋值
3.1 什么是变量解构赋值
ES6 允许按照一定模式从数组和对象中提取值,对变量进行赋值,这被称为解构赋值
3.2 数组的结构
// 1. 数组的结构
const F4 = ['张三','李四','王五','赵六'];
let [zhang, li, wang, zhao] = F4;
console.log(zhang);
console.log(li);
console.log(wang);
console.log(zhao);
3.3 对象的解构
// 2. 对象的解构
const wang = {
name: '王小明',
age: 24,
song: function(){
console.log('会唱青藏高原');
}
};
let {name, age, song} = wang;
console.log(name);
console.log(age);
console.log(song);
song();
四、模板字符串
4.1 什么是模板字符串
模板字符串 是ES6引入新的声明字符串的方式,即增强版的字符串,用反引号( ` )标识
4.2 模板字符串特点
- 字符串中可以直接出现换行符
- 变量拼接,必须使用 ${变量}
// 1. 声明变量
let str = `字符串`;
console.log(str, typeof str);
// 特点1:内容中可以直接出现换行符
let movies = `<ul>
<li>沙丘</li>
<li>绿皮书</li>
<li>白日梦想家</li>
<li>疯狂动物城</li>
</ul>`;
// 特点2: 变量拼接
let likeM = '沙丘';
let out = `我最喜欢的电影是${likeM}`;
console.log(out);
五、 对象的简化写法
ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法
let name = '张小小';
let song = function(){
console.log('会唱Dance Monkey');
}
const game = {
name,
song,
win(){
console.log('获得特等奖');
}
}
console.log(game);
六、箭头函数以及声明特点(重要)
ES6 允许使用箭头 (=>)定义函数
6.1 箭头函数特性
- this 是静态的,this始终指向函数声明时所在的作用域下的this的值 (重要)
- 不能作为构造实例化对象
- 不能使用 arguments 变量
// 特性1: this 是静态的,this始终指向函数声明时所在的作用域下的this的值
function getGame1(){
console.log(this.name);
}
let getGame2 = () =>{
console.log(this.name);
}
window.name = 'switch';
const game = {
name:'老任switch'
}
// 直接调用
getGame1();
getGame2();
console.log('------------');
// call方法调用
getGame1.call(game);
getGame2.call(game);
console.log('------------');
// 特性2: 不能作为构造实例化对象
let Person = (name, age) => {
this.name = name;
this.age =age;
}
let me = new Person('zhang3',22);
console.log(me);
// console.log('------------');
// 特性3:不能使用 arguments 变量
let fn = () =>{
console.log(arguments);
}
fn(1,2,3);
6.2 箭头函数的简写
1)省略小括号,当形参有且只有一个的时候
let add = n => {
return n+n;
}
console.log(add(5));
2)省略花括号,当代码体只有一条语句的时候,此时return 必须省略,且语句的执行结果就是函数的返回值
let pow = n => n * n;
console.log(pow(5));
6.3 箭头函数 - 案例练习
6.3.1 案例1 - 点击 div 2s 后颜色变为红色
箭头函数
// 需求-1 点击 div 2s 后颜色变为红色
let ad = document.getElementById('ad');
ad.addEventListener('click',function(){
setTimeout(() => {
this.style.background = 'red';
},2000);
});
传统方式
let ad = document.getElementById('ad');
ad.addEventListener('click',function(){
let _this = this
setTimeout(function(){
_this.style.background = 'red';
},2000);
});
6.3.2 案例2 - 从数组中返回偶数的元素
箭头函数第一种写法
const result = arr.filter(item => {
if(item % 2 === 0){
return true;
} else {
return false;
}
});
console.log(result);
箭头函数第二种写法
const result = arr.filter(item => item % 2 === 0);
传统方式
const arr = [1,5,8,4,6,12,88,77,73,25];
const result = arr.filter(function(item){
if(item % 2 ===0){
return true;
} else {
return false;
}
});
console.log(result);
6.4 箭头函数应用场景
- 箭头函数适合与 this 无关的回调。例如:定时器,数组的方法回调
- 箭头函数不适合与 this 有关的回调。 例如:事件回调,对象的方法
七、函数参数的默认值设置
ES6 允许给函数参数赋值初始值
1) 形参初始值,具有默认值的参数,一般位置都要靠后(潜规则)
function add(a,b,c=10){
return a + b + c;
}
let result = add(1,2);
console.log(result);
2) 默认值可以与解构以复制结合
function connect({host='127.0.0.1',username,password,port}){
console.log(host);
console.log(username);
console.log(password);
console.log(port);
}
connect({
// host:'localhost',
username:'root',
password:'root',
port:3306
});
八、rest参数
ES6引入 rest 参数,用于获取参数的实参,用来代替arguments
rest参数获取的结果是数组,而ES5 获取的结果是对象
// ES5获取方式
function data1(){
console.log(arguments);
}
data1('王小明','王笑笑','王跑跑');
console.log('---------------');
// ES6 rest参数
function data2(...args){
console.log(args);
}
data2('王小明','王笑笑','王跑跑');
注意:rest参数必须放在参数的最后
function fn(a,b,...args){
console.log(a);
console.log(b);
console.log(args);
}
fn(1,2,3,4,5,6);
九、扩展运算符
( ... ) 扩展运算符能将数组转化为逗号分隔的参数序列
扩展运算符(spread),也就是三个点( ... )。它好比rest参数的逆运算,将一个数组转化为用逗号分隔的参数序列,对数组进行解包
const stars = ['沈腾', '周星驰', '雷佳音'];
function movie(){
console.log(arguments);
}
movie(...stars); // 相当于 movie('沈腾', '周星驰', '雷佳音')
9.1 扩展运算符应用
9.1.1 数组的合并
// 1. 数组合并
const stars1 = ['沈腾', '周星驰'];
const stars2 = ['雷佳音','吴孟达'];
const movie = [...stars1, ...stars2];
console.log(movie);
9.1.2 数组克隆
// 2. 数组克隆
const stars = ['沈腾', '周星驰', '雷佳音'];
const person = [...stars];
console.log(person);
9.1.3 将伪数组转为真正的数组
<div></div>
<div></div>
<div></div>
<script>
// 3. 将伪数组转为真正的数组
const divs = document.querySelectorAll('div');
const divArr = [...divs]
console.log(divArr);
</script>
十、 Symbol
ES6 引入了一中新的原始数据类型 Symbol。表示独一无二的值。它是JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型。
10.1 Symbol 特点
- Symbol 的值是唯一的,用来解决命名冲突问题( 即使 a 和 b 两个变量都等于Symbol(),但两个 Symbol() 也不会相等,即 a 和 b 各自的 Symbol() 所指向的内存地址各不相同。以身份证为例子,symbol()相当于姓名,symbol()的值相当于身份证号码,所以即使姓名相同,但身份证号码也不一样)
- Symbol 值不能与其 他数据进行运算
- Symbol 定义的对象属性不能使用 for...in 循环遍历,但是可以使用 Reflect.ownKeys 来获取对象的所有键名
const a = Symbol();
const b = Symbol();
a !== b; //a和b持有的是两块内存的引用
const c = a;
a === c; //c和a现在指向同一块内存,因为它们保存了同样的地址
10.2 Symbol内置值
除了定义自己使用的Symbol值以外,ES6还提供了11个内置的Symbol值,指向语言内部的使用方法。
注意:Symbol内置值的使用,都是作为某个对象类型的属性去使用
Symbol内置值 | 调用时机 |
Symbol.hasInstance
|
当其他对象使用
instanceof
运算符,判断是否为该对象的实例时,会调用这个方法
|
Symbol.isConcatSpreadable
|
对象的
Symbol.isConcatSpreadable
属性等于的是一个布尔值,表示该对象用于
Array.prototype.concat()
时,是否可以展开
|
Symbol.species
|
创建衍生对象时,会使用该属性
|
Symbol.match
|
当执行
str.match(myObject)
时,如果该属性存在,会调用它,返回该方法的返回值
|
Symbol.replace
|
当该对象被
str.replace(myObject)
方法调用时,会返回该方法的返回值
|
Symbol.search
|
当该对象被
str.search (myObject)
方法调用时,会返回该方法的返回值
|
Symbol.split
|
当该对象被
str.split (myObject)
方法调用时,会返回该方法的返回值
|
Symbol.iterator
|
对象进行
for…of
循环时,会调用
Symbol.iterator
方法,返回该对象的默认遍历器
|
Symbol.toPrimitive
|
该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值
|
Symbol. toStringTag
|
在该对象上面调用
toString
方法时,返回该方法的返回值
|
Symbol. unscopables
|
该对象指定了使用
with
关键字时,哪些属性会被
with
环境排除
|
十一、迭代器
迭代器(Iterator)是一种接口,为各种不同的数据解构提供统一的访问机制。任何数据解构只要部署了 Iterator 接口,就可以完成遍历操作
ES6创造了一中新的遍历命令for...of循环,Iterator 接口主要供 for...of 消费
11.1 原生具备 Iterator 接口的数据(可用 for...of 遍历)
- Array
- Arguments
- Set
- Map
- String
- TypedArray
- NodeList
11.2 工作原理
- 创建一个指针对象,指向当前数据解构的起始位置
- 第一次调用对象的next方法,指针自动指向数据解构的第一个成员
- 接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员
- 每调用next方法韩慧一个包含value和done属性的对象
const stars = ['沈腾','周星星','徐峥','黄渤'];
// 使用for...of遍历
for(let v of stars){
console.log(v);
}
console.log('------迭代器------');
let iterator = stars[Symbol.iterator]();
// 调用对象的next方法
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
11.3 迭代器的应用
注意:需要自定义遍历数据的时候,要想到迭代器
const stars = {
name:'喜剧演员',
person:[
'沈腾',
'周星驰',
'徐峥',
'黄渤'
],
[Symbol.iterator](){
// 索引变量
let index = 0;
let _this = this;
return {
next: function(){
if (index < _this.person.length){
const result = {value: _this.person[index], done: false};
index++;
return result;
} else {
return {value: undefined, done: true};
}
}
}
}
}
// 遍历对象
for(let v of stars){
console.log(v);
}
十二、生成器
生成器函数是ES6提供的一中异步编程解决方案,语法行为与传统函数完全不同
function * gen(){
yield '第一段'; // yield 相当于函数代码的分割符
yield '第二段';
yield '第三段';
}
let iterator = gen();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
12.1 生成器函数的参数传递
- 整体函数可以传递参数
- next方法可以传入实参
function * gen(arg){
console.log(arg);
let one = yield '111';
console.log(one);
let two = yield '222';
console.log(two);
let three = yield '333';
console.log(three);
}
let iterator = gen('aaa');
// next方法可以传入实参
console.log(iterator.next());
console.log(iterator.next('bbb'));
console.log(iterator.next('ccc'));
console.log(iterator.next('ddd'));
12.2 生成器函数实例
12.2.1 案例练习 - 解决回调地狱问题
案例:1s后控制台输出 111,2s后输出222,3s后输出 333
传统写法
// 传统写法
setTimeout(() => {
console.log(111);
setTimeout(() => {
console.log(222);
setTimeout(() => {
console.log(333);
}, 3000);
}, 2000);
}, 1000);
生成器函数 - 解决回调地狱问题
function one(){
setTimeout(() => {
console.log(111);
iterator.next();
}, 1000);
}
function two(){
setTimeout(() => {
console.log(222);
iterator.next();
}, 2000);
}
function three(){
setTimeout(() => {
console.log(333);
iterator.next();
}, 3000);
}
function * gen(){
yield one();
yield two();
yield three();
}
// 调用生成器函数
let iterator = gen();
iterator.next();
12.2.2 案例练习 - 模拟获取 用户数据、订单数据、商品数据
// 模拟按顺序获取 用户数据 订单数据 商品数据
function getUsers(){
setTimeout(() => {
let data = '用户数据';
// 调用next方法,并且将数据传入
iterator.next(data);
}, 1000);
}
function getOrders(){
setTimeout(() => {
let data = '订单数据';
iterator.next(data);
}, 1000);
}
function getGoods(){
setTimeout(() => {
let data = '商品数据';
iterator.next(data);
}, 1000);
}
function * gen(){
let users = yield getUsers();
console.log(users);
let orders = yield getOrders();
console.log(orders);
let goods = yield getGoods();
console.log(goods);
}
// 调用生成器函数
let iterator = gen();
iterator.next();