ES6新特性
ES6官方文档:
参考笔记:https://docs.mphy.top/#/ECMAScript6+/ch01
一、ES6相关介绍
ES全程EcmaScript,是脚本语言的规范,而平时经常编写的JavaScript,是EcmaScript的一种实现,所以ES新特性其实指的就算JavaScript的新特性。
1.1什么是ECMA
ECMA(European Computer Manufacturers Association)中文名称为欧洲计算机制造商协会,这个组织的目标是评估、开发和认可电信和计算机标准。1994 年后该组织改名为Ecma 国际。
1.2什么是ECMAScript
ECMAScript 是由 Ecma 国际通过ECMA-262 标准化的脚本程序设计语言。
1.3什么是 ECMA-262
Ecma 国际制定了许多标准,而ECMA-262 只是其中的一个,所有标准列表查看:http://www.ecma-international.org/publications/standards/Standard.htm
ECMA-262 历史版本查看网址: http://www.ecma-international.org/publications/standards/Ecma-262-arch.htm
- ES5 是 ECMAScript 第5版,2009年发布。
- ES6 是 ECMAScript 第6版,2015年发布,也叫 ES2015。
- 从 ES6 开始,每年发布一个版本,版本号比年份最后一位大 1。
1.4 谁在维护 ECMA-262
TC39(Technical Committee 39)是推进ECMAScript 发展的委员会。其会员都是公司(其中主要是浏览器厂商,有苹果、谷歌、微软、因特尔等)。TC39 定期召开会议,会议由会员公司的代表与特邀专家出席。
1.5 为什么要学ES6
- ES6 的版本变动内容最多,具有里程碑意义
- ES6 加入许多新的语法特性,编程实现更简单、高效
- ES6 是前端发展趋势,就业必备技能
1.6 ES6兼容性
二、ES6新特性
1. let关键字
let
关键字用来声明变量,使用 let
声明的变量有几个特点:
- 不允许重复声明
- 块级作用域
- 不存在变量提升
- 不影响作用域链
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script>
let a;
let b,c,d;
let e = 100;
let f=521,g='iloveyou',h=[];
//1.变量不能重复声明 (会报错)
//而 var 声明变量时,可以重复声明 不报错。
// let star='罗志祥';
// let star='小猪';
//2.块儿级作用域 全局,函数,eval
{
let girl = '小红';
console.log(girl);
}
//外面读取不到
// console.log(girl);
//3.不存在变量提升
//出现undefined 而不报错
console.log(song);
//会报错 Uncaught ReferenceError: Cannot access 'song2' before initialization
console.log(song2);
var song = '恋爱达人';
let song2 = '恋爱达人';
//4.不影响作用域链
{
let school = '清华大学';
function fn(){
console.log(school);
}
fn();
}
</script>
</body>
</html>
应用场景:以后声明变量使用let 就对了
案例1:给多个 div
循环注册点击事件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
.item{
width: 80px;
height: 60px;
border: 1px blue solid;
margin-top: 10px;
/* background-color: aqua; */
}
</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');
//遍历并绑定事件
//var i=0;i<items.length;i++ 就不行,需要讲var改为let
for(let i=0;i<items.length;i++){
items[i].onclick = function(){
//修改当前元素的背景颜色
// this.style.background = 'pink';
items[i].style.background = 'pink';
}
}
</script>
</body>
</html>
2. const 关键字
const
关键字用来声明常量,const
声明有以下特点:
- 声明必须赋初始值
- 标识符一般为大写
- 不允许重复声明
- 值不允许修改
- 块级作用域
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script>
//声明常量
const SCHOOL = '清华大学';
//1.一定要赋初始值
//要不然会报错
//Missing itializer in const declaration (at new_file3.html:13:10)
// const A;
//2.一般常量使用大写(潜规则)
const a = 100; //小写也不会报错
console.log(a);
//3.常量的值不能修改
//Assignment to constant variable.
//以下会报错
// SCHOOL = 'QingHua';
//4.块级作用域
{
const PLAYER = 'uzi';
console.log(PLAYER);
}
// console.log(PLAYER); //这样会报错
//5.对于数组和对象的元素修改,不算做对常量的修改,不会报错
const TEAN = ['UZI','MXLG','Ming'];
TEAN.push('Meiko'); //这样不会报错
TEAN = 100; //这样会报语法错误
</script>
</body>
</html>
3.变量的解构赋值
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为 解构赋值。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script>
//1.数组的解构
const F4 = ['小沈阳','刘能','赵四'];
let [xiao,liu,zhao] = F4;
console.log(xiao);
console.log(liu);
console.log(zhao);
//2.对象的解构
const zhao = {
name:'赵本山',
age:'不祥',
xiaopin: function(){
console.log("我可以演小品");
}
};
let {name , age ,xiaopin} = zhao;
console.log(name);
console.log(age);
console.log(xiaopin);
xiaopin();
//讲xiaopin 解构出来,为了让其书写方便,减少冗余。
// let {xiaopin} = zhao;
// xiaopin();
//不解构 需要这样写
zhao.xiaopin();
</script>
</body>
</html>
4.模板字符串
模板字符串(template string)是增强版的字符串,用反引号 ` 标识,特点:
- 字符串中可以出现换行符
- 可以使用
${xxx}
形式输出变量
应用场景:当遇到字符串与变量拼接的情况使用模板字符串。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script>
//es6引入新的声明字符串的方式 [``] '' ""
//1.声明
let str = `我也是一个字符串哦!`;
console.log(str,typeof str);
// 输出结果: 我也是一个字符串哦! string
//2.内容中可以直接出现换行符
let str1 = `<ul>
<li>沈腾</li>
<li>玛丽</li>
<li>艾伦</li>
</ul>`
//3. 变量拼接
let lovest = '艾伦';
let out = `${lovest} 是我心目中最搞笑的演员!`;
console.log(out);
//变量拼接时 必须使用 ${}
</script>
</body>
</html>
5.对象的简化写法
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script>
//ES6允许在大括号里面,直接写入变量和函数,作为对象的属性和方法
let name ='清华大学';
let change = function(){
console.log('我们可以改变世界!');
}
const school ={
// name:name, //这写法和下面意思一样
name, //这是上面的简写
change,
// improve:function(){} //以下是简化
improve(){
console.log("我们可以提高你的技能");
}
}
console.log(school);
school.improve();
</script>
</body>
</html>
6.箭头函数以及声明特点
ES6 允许使用「箭头」=>
定义函数。
- function 写法:
function fn(param1, param2, …, paramN) {
// 函数体
return expression;
}
- => 写法:
let fn = (param1, param2, …, paramN) => {
// 函数体
return expression;
}
箭头函数的 注意点:
- 如果形参只有一个,则小括号可以省略
- 函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的执行结果
- 箭头函数
this
始终指向声明时所在作用域下this
的值 - 箭头函数不能作为构造函数实例化
- 不能使用
arguments
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script>
//es6 允许使用【箭头】 ( => ) 定义函数。
//声明一个函数
let fn = function(){
}
//声明箭头函数
let fn1 = (a,b)=>{
return a+b;
}
let result = fn1(1,2);
console.log(result);
//1.this 是静态的 this始终指向函数声明时所在作用域下的this的值
function getName(){
console.log(this.name);
}
let getName2 = ()=>{
console.log(this.name);
}
//设置window 对象的 name属性
window.name = '清华同方';
const school = {
name: "ATGUIGU"
}
//直接调用
getName(); //清华同方
getName2(); //清华同方
//call 方法调用
getName.call(school); //ATGUIGU
getName2.call(school); //清华同方
//2.不能作为构造实例化对象
//以下汇报 person is not a constructor 错误
/* let person = (name,age)=>{
this.name = name;
this.age = age;
}
let me = new person('xiao',30);
console.log(me); */
//3.不能使用 arguments 变量
/* let fn2 =()=>{
console.log(arguments);
}
fn2(1,2,3); */ // arguments is not defined
//4.箭头函数的简写
//1)省略小括号,当形参有且只有一个的时候
let add = n =>{
return n + n;
}
console.log(add(5)); //10
//2)省略花括号,当代码体只有一条语句的时候,此时return必须省略
//而且语句的执行结果就是函数的返回值
// let pow = (n) =>{
// return n*n;
// };
let pow = (n) => n * n;
console.log(pow(6)); //36
</script>
</body>
</html>
7.箭头函数的实践与应用
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
#ad{
width: 200px;
height: 200px;
background:blue;
}
</style>
</head>
<body>
<div id="ad"></div>
<script>
//案例1 点击 div 2s 后颜色变成 粉色
//获取元素
let ad = document.getElementById('ad');
//绑定事件
ad.addEventListener("click",function(){
//保存this的值
let _this = this;
//定时器
setTimeout(function(){
console.log(_this)
// ad.styl e.background = 'pink';
//_this 代表 ad
_this.style.background='pink'
},2000);
})
</script>
</body>
</html>
8.函数参数的默认值设置
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script>
//ES6 允许给函数参数赋初始值
//1.形参初始值 具有默认值的参数,一般位置要靠后(潜规则)
function add(a,b,c=10){
return a+b+c;
}
// let result = add(1,2,3);//6
let result = add(1,2);//13
console.log(result);
//2.与解构赋值结合
function connect({host='127.0.0.1',username,password}){
console.log(host); //localhost
console.log(username); //root
console.log(password); //root
}
connect({
host:'localhost',
username:'root',
password:'root'
})
</script>
</body>
</html>
9.rest参数
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script>
//es6引入rest 参数,用于获取函数的实参,用来代替 arguments
//es5获取实参的方式
function date(){
console.log(arguments);
}
date('百汇','想念','思慧');
//运行结果 Arguments(3) ['百汇', '想念', '思慧']
//rest 参数
function date1(...args){
console.log(args);
}
date1('悟空','八戒','沙僧');
// ['悟空', '八戒', '沙僧'] 这样运行结果为数组
//rest 参数必须要放到参数最后
function fn(a,b,...args){
console.log(a); // 1
console.log(b); //2
console.log(args); // [3, 4, 5, 6]
}
fn(1,2,3,4,5,6);
//运行结果: 1
// 2
// [3, 4, 5, 6]
</script>
</body>
</html>
10.扩展运算符的介绍
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script>
// ... 扩展运算符能将 [数组] 转换未逗号分隔的 [参数序列]
//声明一个数组
const tfboys = ['易烊千玺','王源','王俊凯'];
//声明一个函数
function chunwan(){
console.log(arguments);
}
chunwan(...tfboys);
// 输出结果:Arguments(3) ['易烊千玺', '王源', '王俊凯', callee: ƒ, Symbol(Symbol.iterator): ƒ]
chunwan(tfboys);
//Arguments [Array(3), callee: ƒ, Symbol(Symbol.iterator): ƒ]
//0: (3) ['易烊千玺', '王源', '王俊凯']
</script>
</body>
</html>
14.扩展运算符应用
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div></div>
<div></div>
<div></div>
<script>
//1.数组的合并
const kuaizi = ['王太利','肖央'];
const fenghaung = ['曾意','玲花'];
const zhixuanzuhe = kuaizi.concat(fenghaung); //es5 中的标准
console.log(zhixuanzuhe); // ['王太利', '肖央', '曾意', '玲花']
const zhuixuanzuhe2 = [...kuaizi,...fenghaung];
console.log(zhixuanzuhe); // ['王太利', '肖央', '曾意', '玲花']
//2.数组的克隆
const sanzhihua = ['E','G','M'];
const sanyecao = [...sanzhihua];
console.log(sanyecao); // ['E', 'G', 'M']
//3.讲伪数组转为真正的数组
const divs = document.querySelectorAll('div');
const divArr = [...divs];
console.log(divs); //NodeList(3) [div, div, div]
console.log(divArr); //[div, div, div]
</script>
</body>
</html>
11.Symbol
11.1 Symbol的介绍与创建
ES6 引入了一种新的原始数据类型 Symbol
,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型。
JavaScript 的七种基本数据类型:
- 值类型(基本类型):string、number、boolean、undefined、null、symbol
- 引用数据类型:object(包括了array、function)
Symbol 的特点:
- Symbol 的值是唯一的,用来解决命名冲突的问题
- Symbol 值不能与其他数据进行运算
- Symbol 定义的对象属性不能使用
for...in
循环遍历,但是可以使用Reflect.ownKeys
来获取对象的所有键名
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script>
//创建Symbol
let s = Symbol();
console.log(s,typeof s); //Symbol() 'symbol'
let s2 = Symbol('清华大学');
let s3 = Symbol('清华大学');
console.log( s2 === s3); //false
let s4 = Symbol.for('清华大学');
let s5 = Symbol.for('清华大学');
console.log( s4 === s5); //true
//不能与其他数据进行运算 (以下这些都会报错)
let result = s + 100;
let result = s > 100;
let result = s + s;
//数据类型
//USONB
//u undefined
//s string symbol
//o object
//n null number
//b boolean
</script>
</body>
</html>
11.2 对象添加Symbol类型的属性
案例:安全的向对象中添加属性和方法。
分析:如果直接向对象中添加属性或方法,则原来对象中可能已经存在了同名属性或方法,会覆盖掉原来的。所以使用 Symbol
生成唯一的属性或方法名,可以更加安全的添加。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script>
// 这是一个 game 对象,假设我们不知道里面有什么属性和方法
//向对象中添加方法 up down
const 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);
//{name: '俄罗斯方块', up: ƒ, down: ƒ, Symbol(): ƒ, Symbol(): ƒ}
let youxi = {
name:'狼人杀',
[Symbol('say')]:function(){
console.log("我可以发言");
},
[Symbol('zibao')]:function(){
console.log("我可以自爆");
}
}
console.log(youxi);
//{name: '狼人杀', Symbol(say): ƒ, Symbol(zibao): ƒ}
</script>
</body>
</html>
11.3 Symbol的内置属性
除了定义自己使用的 Symbol 值以外,ES6 还提供了11 个内置的 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 环境排除。 |
案例1:Symbol.hasInstance
方法判断是否属于这个对象时被调用。
class A {
static [Symbol.hasInstance]() {
console.log('判断是否属于这个对象时被调用');
}
}
let obj = {};
console.log(obj instanceof A)
// 判断是否属于这个对象时被调用
// false
案例2:数组使用 concat
方法时,是否可以展开。
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
let arr3 = [4, 5, 6];
arr2[Symbol.isConcatSpreadable] = false;
console.log(arr1.concat(arr2));
// [ 1, 2, 3, [ 4, 5, 6, [Symbol(Symbol.isConcatSpreadable)]: false ] ]
console.log(arr1.concat(arr3));
// [ 1, 2, 3, 4, 5, 6 ]
12.迭代器 iterator
12.1定义
遍历器(Iterator
)就是一种机制。它是一种接口,为各种不同的数据结构提 供统一的访问机制。任何数据结构只要部署 Iterator
接口,就可以完成遍历操作。
- ES6 创造了一种新的遍历命令
for...of
循环,Iterator
接口主要供for...of
消费。 - 原生具备
iterator接口的数据(可用for of 遍历)
Array
Arguments
Set
Map
String
TypedArray
NodeList
案例:使用 next()
方法遍历原生自带 iterator
接口的数据:
//声明一个数组
const xiyou = ['唐僧','孙悟空','猪八戒','沙僧'];
//使用 for... of 遍历数组
for(let v of xiyou){
console.log(v)
}
//唐僧 孙悟空 猪八戒 沙僧
//使用 for...in 遍历数组
for(let v in xiyou){
console.log(v)
}
// 0
// 1
// 2
// 3
// 遍历数组
let xiyou = ['唐僧','孙悟空','猪八戒','沙僧'];
let iter2 = xiyou[Symbol.iterator]();
console.log(iter2.next()); // { value: '唐僧', done: false }
console.log(iter2.next()); // { value: '孙悟空', done: false }
console.log(iter2.next()); // { value: '猪八戒', done: false }
console.log(iter2.next()); // { value: '沙僧', done: false }
上面的案例只是为了证明他们自带 iterator
接口,实际上直接使用 for...of
方法遍历即可(iterator
接口为 for...of
)服务。例如,可以使用 for [k, v] of map
来遍历 Map 数据结构中的键和值。
const mp = new Map();
mp.set('a', 1);
mp.set('b', 2);
mp.set('c', 3);
for (let [k, v] of mp) {
console.log(k, v);
}
/*
a 1
b 2
c 3
*/
12.2工作原理
- 创建一个指针对象,指向当前数据结构的起始位置
- 第一次调用对象的
next
方法,指针自动指向数据结构的第一个成员 - 接下来不断调用
next
方法,指针一直往后移动,直到指向最后一个成员 - 每调用
next
方法返回一个包含value
和done
属性的对象
应用场景:需要自定义遍历数据的时候,要想到迭代器。
12.3 自定义遍历数据
我们可以通过给数据结构添加自定义 [Symbol.iterator]()
方法来使该数据结构能够直接被遍历,从而使 for...of
能够直接遍历指定数据,达到为 for...of
服务的功能。
// 需求:遍历对象中的数组
const xiaomi = {
uname: '小明',
course: [ '高数', '大物', '英语', '数据库' ],
// 通过自定义 [Symbol.iterator]() 方法
[Symbol.iterator]() {
// 初始指针对象指向数组第一个
let index = 0;
// 保存 xiaomi 的 this 值
let _this = this;
return {
next: function () {
// 不断调用 next 方法,直到指向最后一个成员
if (index < _this.course.length) {
return { value: _this.course[index++], done: false };
} else {
// 每调用next 方法返回一个包含value 和done 属性的对象
return { value: undefined, done: true };
}
}
}
}
}
// for...of直接遍历达到目的
for (let v of xiaomi) {
console.log(v);
}
//高数
//大物
//英语
//数据库