1 ECMAcript相关介绍
1.1 什么是ECMA
ECMA(European Computer Manufacturers Association)中文名称为欧洲计算机制造商协会,这个组织的目标是评估、开发和认可电信和计算机标准。1994年后该组织改名为Ecma国际。
1.2 什么是ECMScript
ECMAScript是由Ecma国际通过ECMA-262标准化的脚本程序设计语言。
1.3 什么是ECMA-262
Ecma国际制定了许多标准,而ECMA-262只是其中的一个
1.4 ECMA-262历史
ECMA-262(ECMAScript)历史版本查看网址
1.5 谁在维护ECMA-262
TC39(Technical Committee 39)是推进ECMAScript发展的委员会。其会员都是公司(其中主要是浏览器厂商,有苹果、谷歌、微软、因特尔等)。TC39定期
召开会议,会议由会员公司的代表与特邀专家出席
1.6 为什么要学习ES6
- ES6的版本变动内容最多,具有里程碑意义
- ES6加入许多新的语法特性,编程实现更简单、高效
- ES6是前端发展趋势,就业必备技能
1.7 ES6兼容性
http://kangax.github.io/compat-table/es6/可查看兼容性
2 ECMASript 6新特性
2.1 let关键字
let关键字用来声明变量,使用let声明的变量有几个特点:
- 不允许重复声明
- 块儿级作用域
- 不存在变量提升
- 不影响作用域链
应用场景:以后声明变量使用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>Document</title>
</head>
<body>
<script>
// 声明变量
let a;
let b, c, d;
let e = 100;
let f = 521, g = 'iloveyou', h = [];
// 1. 变量不能重复声明
// let star = '刘德华';
// let star = '张学友';
// 2. 块级作用域 全局 函数 eval
// {
// let girl = '刘诗诗';
// }
// console.log(girl);
// 3. 不存在变量提升
// console.log(song);
// let song = '哈哈哈';
// 4. 不影响作用域链
{
let school = 'shangguigu';
function fn() {
console.log(school);
}
fn();
}
</script>
</body>
</html>
let经典案例
点击div切换颜色
<!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>Document</title>
<style>
.item {
width: 100px;
height: 50px;
border: solid 1px rgb(42, 156, 156);
float: left;
margin-right: 10px;
}
</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>
// 获取div元素对象
let items = document.getElementsByClassName('item');
// 遍历并绑定事件
for (let i = 0; i < items.length; i ++ ) {
items[i].onclick = function() {
// 修改当前元素的背景颜色
items[i].style.background = 'pink';
}
}
</script>
</body>
</html>
2.2 const关键字
const关键字用来声明常量,const 声明有以下特点
- 声明必须赋初始值
- 标识符一般为大写
- 不允许重复声明
- 值不允许修改
- 块儿级作用域
注意:对象属性修改和数组元素变化不会出发const错误
应用场景:声明对象类型使用
const,非对象类型声明选择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>const 定义常量</title>
</head>
<body>
<script>
// 声明常量
const name = 'yaya';
// console.log(name);
// 一定要赋初值
// const A;
// 2. 一般常量使用大小写(潜规则)
// const a = 100;
// 3. 常量的值不能修改
// NAME = 'mmm';
// 4. 块级作用域
// {
// const PLAYER = 'UZI';
// }
// console.log(PLAYER);
// 5. 对于数组和对象的元素修改,不算做对常量的修改,不会报错
const TEAM = ['UZI', 'MXLG', 'Ming', 'Letme'];
TEAM.push('Meiko');
console.log(TEAM);
</script>
</body>
</html>
2.3 变量的解构赋值
ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。
- 数组的解构赋值
const arr = ['张学友', '刘德华','黎明', '郭富城'];
let [zhang, Liu, Li, guo] = arr;
- 对象的解构赋值
const lin = {
name: '林志颖',
tags: ['车手', '歌手', '小旋风', '演员']
}
let {name, tags} = lin;
- 复杂解构
let wangfei = {
name: ' 王菲',
age: 18,
songs: ['红豆', '流年',' 暧昧','传奇'],
history: [
{name: ' 窦唯'},
{name: '李亚鹏'},
{name: '谢霆锋'}
]
};
let {songs: [one, two, three], history: [first, second, third]} = wangfei;
注意:频繁使用对象方法、数组元素,就可以使用解构赋值形式
eg:
<!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>变量的解构赋值</title>
</head>
<body>
<script>
// ES6允许按照一定模式从数组和对象中提取值,对变量进行赋值 这被称为解构赋值
// 1. 数组的解构
// const F4 = ['小沈阳', '刘能', '赵四', '宋小宝'];
// let [xiao, liu, zhao, song] = F4;
// console.log(xiao);
// console.log(liu);
// console.log(zhao);
// console.log(song);
// 2. 对象的解构
const zhao = {
name: '赵本山',
age: '不详',
xiaopin: function() {
console.log('我可以演小品');
}
};
// let {name, age, xiaopin} = zhao;
// console.log(name);
// console.log(age);
// console.log(xiaopin);
// xiaopin();
let {xiaopin} = zhao;
xiaopin();
</script>
</body>
</html>
2.4 模板字符串
模板字符串(template string)是增强版的字符串,用反引号(`)标识,特点:
- 字符串 中可以出现换行符
- 可以使用${xx}形式输出变量
// 定义字符串
let str = `<ul>
<li>沈腾</li>
<li>玛丽</li>
<li>魏翔</li>
<li>艾伦</li>
</ul>`;
// 变量拼接
let lovest = '魏翔';
let out = `${lovest}是我心目中最搞笑的演员!!`;
注意:当遇到字符串与变量拼接的情况使用模板字符串
eg:
<!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>Document</title>
</head>
<body>
<script>
// ES6引入新的声明字符串的方式 [``] '' ""
// 1. 声明
// let str = `我也是一个字符串哦!`;
// console.log(str, typeof str);
// 2. 内容中可以直接出线换行符
let str = `<ul><li>沈腾</li><li>玛丽</li><li>魏翔</li><li>艾伦</li></ul>`;
// 3. 变量拼接
let lovest = '魏翔';
let out = `${lovest}是我心目中最搞笑的演员!!`;
console.log(out);
</script>
</body>
</html>
2.5 简化对象写法
ES6允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
let name = 'yaya';
let change = function() {
console.log('我们可以改变你!!!');
}
const school = {
name,
change,
improve() {
console.log("我们可以提高你的技能");
}
}
2.6 箭头函数
ES6 允许使用「箭头」 (=>)定义函数。
// 1. 通用写法
1et fn = (arg1, arg2, arg3) => {
return arg1 + arg2 + arg3;
}
箭头函数的注意点:
- 如果形参只有一个,则小括号可以省略
- 函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的执行结果
- 箭头函数this指向声明时所在作用域下this的值
- 箭头函数不能作为构造函数实例化
- 不能使用arguments
// 2.省略小括号的情况
let fn2 = num => {
return num * 10;
};
// 3.省略花括号的情况
let fn3 = score => score * 20;
// 4. this指向声明时所在作用域中this的值
let fn4 = () => {
console.log(this);
}
let school = {
name: 'yaya' ,
getName() {
let fn5 = () => {
console.log(this);
}
fn5();
}
};
注意:箭头函数不会更改this指向,用来指定回调函数会非常合适
箭头函数实践
<!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>Document</title>
<style>
div {
width: 200px;
height: 200px;
background: #58a;
}
</style>
</head>
<body>
<div id="ad"></div>
<script>
// 需求1 点击div 2s后颜色变成粉色
// 获取元素
let ad = document.getElementById('ad');
// 绑定事件
ad.addEventListener('click', function() {
// let _this = this;
// 定时器
setTimeout(() => {
// 修改背景颜色 this
// _this.style.background = 'pink';
this.style.background = 'pink';
}, 2000);
});
// 需求2 从数组中返回偶数的元素
const arr = [1, 6, 9, 10, 100, 25];
// const result = arr.filter(function(item) {
// if (item % 2 === 0) {
// return true;
// } else {
// return false;
// }
// });
const result = arr.filter(item => item % 2 === 0);
console.log(result);
// 箭头函数适合与this无关的回调 定时器 数组的方法回调
// 箭头函数不适合与this有关的回调 事件水貂 对象的方法
</script>
</body>
</html>
2.7 ES6允许函数参数赋值初始值
<!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>Document</title>
</head>
<body>
<script>
// 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
})
</script>
</body>
</html>
2.8 rest参数
ES6引入rest参数,用于获取函数的实参,用来代替arguments
// 作用与arguments类似
function add(. ..args){
console.log(args);
}
add(1, 2, 3, 4, 5);
// rest 参数必须是最后一个形参
function minus(a,b, ...args){
console.log(a, b, args);
}
minus(100, 1, 2, 3, 4, 5, 19);
注意: rest参数非常适合不定个数参数函数的场景
2.9 spread扩展运算符
扩展运算符(spread)也是三个点(…)它好比rest参数的逆运算,将一个数组转为逗号分隔的参数序列,对数组进行解包
// 展开数组
const tfboys = ['易烊千玺', '王源', '王俊凯'];
// 声明一个函数
function chunwan() {
console.log(arguments);
}
chunwan(...tfboys);
// 展开对象
let skillOne = {
q: '致命打击',
};
let skillTwo = {
w: '勇气'
};
let skillThree = {
e: '审判'
};
let skillFour = {
r: '德玛西亚正义'
};
let gailun = { ...skilLOne, ...skilLTwo, ...skillThree, ...skillFour};
2.10 扩展运算符应用
eg:
<!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>扩展运算符应用</title>
</head>
<body>
<div></div>
<div></div>
<div></div>
<div></div>
<script>
// 1. 数组的合并
// const kuaizi = ['王太利', '肖央'];
// const fenghuang = ['曾毅', '玲花'];
// const zuixuanxiaopingguo = kuaizi.concat(fenghuang);
// const zuixuanxiaopingguo = [...kuaizi, ...fenghuang];
// console.log(zuixuanxiaopingguo);
// 2. 数组的克隆
// const sanzhihua = ['E', 'G', 'M'];
// const sanyecao = [...sanzhihua];
// console.log(sanyecao);
// 3. 将伪数组转换为真正的数组
const divs = document.querySelectorAll('div');
const divArr = [...divs];
console.log(divArr);
</script>
</body>
</html>
2.11 Symbol
https://www.cnblogs.com/shenjp/p/7143706.html
2.11.1. Symbol基本使用
ES5的对象属性名都是字符串,这就很容易造成属性名的冲突,比如一个项目很庞大,又不是一个人开发 的,就有可能会造成变量名冲突,如果有一个独一无二的名字就好了,这样就能从根本上防止属性名冲突。这就是ES6引入Symbol的原因。
ES6引入的是一种新的原始数据类型Symbol,表示独一无二的,它是JavaScript的第七种数据类型。Symbol值通过Symbol函数生成,只要属性名是属于Symbol类型的,就是独一无二的,可以保证不会与其他属性名产生冲突。
Symbol特点
- Symbol的值是唯一的,用来解决命名冲突的问题
- Symbol 值不能与其他数据进行运算
- Symbol 定义的对象属性不能使用for…in循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名
//创建Symbol
let s1 = Symbol();
console.log(s1, typeof s1);
//添加标识的Symbol
let s2 = Symbol('yaya');
let s2_2 = SymboL('yaya');
console.log(s2 === s2_2);
//使用Symbol for定义
let s3 = Symbol.for('yaya');
let s3_2 = Symbol.for('yaya');
console.log(s3 === s3_2);
eg:
<!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>Document</title>
</head>
<body>
<script>
// 创建Symbol
let s = Symbol();
// console.log(s, typeof s);
let s2 = Symbol('yaya');
let s3 = Symbol('yaya');
// Symbol.for 创建
let s4 = Symbol.for('yaya');
let s5 = Symbol.for('yaya');
// 不能与其他数据进行运算
// let result = s + 100;
// let result = s > 100;
// let result = s + s;
// USONB you are so niubility
// u undefined
// s string symbol
// o object
// n null number
// b boolean
</script>
</body>
</html>
eg:
<!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>Document</title>
</head>
<body>
<script>
// 向对象中添加方法 up down
// let game = {
// name:'俄罗斯方块',
// up: function(){},
// down: function(){}
// };
// // 声明一个对象
// let methods = {
// up: Symbol(),
// down: Symbol()
// };
// game[methods.up] = function() {
// console.log('我可以改变形状');
// }
// game[methods.down] = function() {
// console.log('我可以快速下降!!');
// }
// console.log(game);
let youxi = {
name: '狼人杀',
[Symbol('say')]: function() {
console.log('我可以发言');
},
[Symbol('zibao')]: function() {
console.log('我可以自爆');
}
}
console.log(youxi);
</script>
</body>
</html>
2.11.2 Symbol内置值
除了定义自己使用的Symbol值以外,ES6还提供了11个内置的Symbol值,指向语言内部使用的方法。可以称这些方法为魔术方法,因为它们会在特定的场景下自动执行。
<!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>Symbol内置属性</title>
</head>
<body>
<script>
// class Person {
// static [Symbol.hasInstance](param) {
// console.log(param);
// console.log('我被用来检测类型了');
// return true;
// }
// }
// let o = {};
// console.log(o instanceof Person);
const arr = [1, 2, 3];
const arr2 = [4, 5, 6];
arr2[Symbol.isConcatSpreadable] = false;
console.log(arr.concat(arr2));
</script>
</body>
</html>
2.12 迭代器
遍历器(Iterator)就是一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作。
- ES6 创造了一种新的遍历命令for…of循环,Iterator 接口主要供for…of消费
- 原生具备iterator接口的数据(可用for of遍历)
a) Array
b) Arguments
c) Set
d) Map
e) String
f) TypedArray
g) NodeList
- 工作原理
- 创建一个指针对象,指向当前数据结构的起始位置
- 第一次调用对象的next方法,指针自动指向数据结构的第一个成员
- 接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员
- 每调用next方法返回一个包含value和done属性的对象
需要自定义遍历数据的时候,要想到迭代器
<!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>迭代器</title>
</head>
<body>
<script>
// 声明一个数组
const xiyou = ['唐僧', '孙悟空', '猪八戒', '沙僧'];
// 使用for...of遍历数组
// for (let v of xiyou) {
// console.log(v);
// }
let iterator = xiyou[Symbol.iterator]();
//调用对象的next方法
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
</script>
</body>
</html>
eg:
<!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>自定义遍历数据</title>
</head>
<body>
<script>
// 声明一个对象
const banji = {
name: "终极一班",
stus: [
'xiaoming',
'xiaoning',
'xiaotian',
'knight'
],
[Symbol.iterator]() {
// 索引变量
let index = 0;
let _this = this;
return {
next: function() {
if (index < _this.stus.length) {
const result = {value: _this.stus[index], done: false};
// 下标自增
index++;
// 返回结果
return result;
} else {
return {value: undefined, done: true};
}
}
};
}
}
// 遍历这个对象
for (let v of banji) {
console.log(v);
}
</script>
</body>
</html>
2.13 生成器
生成器函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同
function * gen() {
// console.log(111);
yield '一只没有耳朵';
// console.log(222);
yield '一只没有尾部';
// console.log(333);
yield '真奇怪';
// console.log(444);
}
let iterator = gen();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
代码说明:
-
- 的位置没有限制
- 生成器函数返回的结果是迭代器对象,调用迭代器对象的next方法可以得到yield语句后的值
- yield 相当于函数的暂停标记,也可以认为是函数的分隔符,每调用一次next方法,执行一段代码
- next方法可以传递实参,作为yield语句的返回值
eg:
<!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>生成器函数参数</title>
</head>
<body>
<script>
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');
console.log(iterator.next());
// next方法可以传入实参
console.log(iterator.next('BBB'));
console.log(iterator.next('CCC'));
console.log(iterator.next('DDD'));
</script>
</body>
</html>
2.13.1 生成器函数实例1
<!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>生成器函数实例</title>
</head>
<body>
<script>
// 异步编程 文件操作 网络操作(ajax, request) 数据库操作
// 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();
</script>
</body>
</html>
2.13.2 生成器函数实例2
<!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>生成器函数</title>
</head>
<body>
<script>
// 模拟获取 用户数据 订单数据 商品数据
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();
</script>
</body>
</html>
s() {
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();
</script>
```