目标
学习完 ES6 可以掌握方便后续的开发,未来工作中大量使用 ES6 开发
1. ECMAScript 6 介绍
ES – ECMAScript
ECMA – 欧洲计算机制造商协会
ECMA262标准 – 第一版JS的标准
我们之前学习的是 ES5.1
2015年发布了ES6,并且更名为ES2015。决定以年号为版本号,并且每年发布一个新版本。
- ES2015
- ES2016
- …
网上所说的ES7、ES8等等,只是人们推算的,因为官方从未承认什么ES7,ES8。网上所提到的ES7,正式名称应该是ES2016。
- 每一年,所有人可以给JS提议,提出JS需要哪些改进,并给出解决办法。
- 如果提议被采纳,进入草案阶段
- 第二年发布的新版,就会包括前一年的草案
1.1 为什么要学习ES6
- 提供了更加方便的新语法,弥补 JS 语言本身的缺陷,新增了便捷的语法
- 给内置对象(Array、String、…)增加了更多的方法。
- ES6 让 JS 可以开发复杂的大型项目,成为企业级开发语言
- 新的前端项目中大量使用 ES6 的新语法(今天学完,后面就大量使用了)
1.2 ECMAScript 6 是什么
- ECMAScript 6 又叫 ES2015,简称 ES6
- ES6 是继 ES4、ES5 之后的 JS 语言规范
- ES6 中增加了一些新的特性
- ES6 的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言
- 2015年6月发布
1.3 小结
- ES6 是新的 JS 的代码规范,提供了一些新特性,使我们可以开发大型应用
- ES6 弥补了 JS 语言本身的缺陷,增加了新语法,扩展了内置对象
- ES6用起来非常爽。因为语法非常人性化。
2. ECMAScript 6 新增语法
- let和const关键字
- 箭头函数
- 函数参数默认值
- 函数剩余参数
- Array对象扩展
- String对象扩展
- 新增对象Set
- 定义对象的简洁方式
- Promise(最后一天)
2.1 let 和 const
let – 用于声明变量
- let 定义变量,只要有let参与,不能重复声明变量,会报错。但可以改变其值
let c = 10;
c = 20;
console.log(c);
- 具有块级作用域
for (let i = 0; i < 3; i++) {
//只能在这里面打印才有效
console.log(i);
}
- 没有变量提升,必须先定义再使用
let a = 10;
console.log(a);
- let声明的变量,不能通过window调用,调用显示undefined
let b = 300;
console.log(window.b); //undefined
注意:
- 如果使用var声明了变量,也不能再次用let声明了,反之也是不行的。
- 原因也是这个变量已经被声明过了。
- 不过这只是一种特殊情况了,实际开发要么全部使用var,要么全部使用let。
const – 用于声明常量(值不能改变)
- 使用const关键字定义常量
- 常量是不可变的,一旦定义,则不能修改其值
- 初始化常量时,必须给初始值
- 具有块级作用域
- 没有变量提升,必须先定义再使用
- 常量也是独立的,定义后不会压入到window对象中
代码演示:
// 1. 使用const关键字定义常量,常量名一般大写
const PI = 3.1415926;
// 2. 常量是不可变的,一旦定义,则不能修改其值
const PI = 3.1415926;
PI = 3.14; // 报错,常用一旦被初始化,则不能被修改
// 3. 初始化常量时,必须给初始值
const PI; // 报错,Missing initializer in const declaration
// 4. 具有块级作用域
// 5. 没有变量提升,必须先定义再使用
// 6. 常量也是独立的,定义后不会压入到window对象中
// 这三条和let变量一样,不再写代码
let 和 const 小结
2.2 解构赋值
- 数组的解构
- 对象的解构
ES 6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
2.2.1 数组的解构
语法:
let 模式 = 数组 //模式跟数组长得一样
- 特点1.将数组中的值取出,赋值给变量
let [a, b, c] = [1, 2, 3];
console.log(a,b,c);
- 特点2.数组的值比变量多
let [d, e, f] = [2, 3, 4, 5, 6];
console.log(d, e, f); //打印出 2,3,4
- 特点3.变量的数量比值多
let [g, h, v] = [1, 2];
console.log(g, h, v); // 1, 2, undefined
- 特点4.按需取值
let [name, , data] = ['关羽', '美髯公', '青龙偃月刀']; //用空格空出,逗号不可以省略
console.log(name, data);
- 特点5.剩余值
let [aa, bb, ...cc] = [1,2, 3, 4, 5, 6];
console.log(aa, bb); // aa:1; bb:2; cc: 3,4,5,6
console.log(cc); // aa:1; bb:2; cc: 3,4,5,6
- 特点6.误区
let [aaa, ...bbb, ccc] = [1, 2, 3, 4, 5 ,6]; // 报错,剩余值,必须是最后一个元素
- 实际小应用
<body>
<script>
// 定义一个函数,函数的参数是一个数组,我们要实现的是把数组的前三个单元加起来
// 第一种方法:
function sum(arr) {
let [a, b, c] = arr;
console.log(a + b + c);
};
sum([1, 2, 3, 4, 5]);
// 第二种较简单方法:
function sum([a, b, c]) {
// 形参 = 实参
// [a, b, c] = [1, 2, 3, 4, 5]
console.log(a + b + c);
}
sum([2, 3, 4, 5, 6]);
</script>
</body>
2.2.2 对象的解构
意思是把对象中的值取出来,赋值给其他变量
- 特点1:变量名和属性名一致即可获取到值,不一定要一一对应
let {name, id, nickname} = {id: 1, name: '宋江', nickname: '及时雨'};
console.log(id, name, nickname);
- 特点2. 值多,变量少
let {id, nickname} = {id: 2, name: '露露', nickname: '玉麒麟'};
console.log(id, nickname);
- 特点3. 变量多,值少
let {id, name, age} = {id: 3, name: '吴用'};
console.log(id, name, age);
- 特点4. 剩余值
let {id, age, ...other} = {id:4, name: '林冲', nickname: '豹子头', age: 35};
console.log(id, age);
console.log(other); // {name: '林冲', nickname: '豹子头'}
对象的复杂解构
如果在解构的时候,变量的名字如果有冲突。则可以使用 a:b 的形式,为变量改名
代码演示:
<body>
<script>
let person = {
name: '小明',
age: 25,
dog: {
name: '小黑',
age: 2
}
}
// 使用对象的解构,把人的名字和小狗的名字解出来
//如果在解构的时候,变量的名字如果有冲突。则可以使用 a:b 的形式,为变量改名
let {name:pname, age:page, dog:{name, age}} = person;
console.log(pname, page, name, age);
</script>
</body>
对象更加的复杂解构
- 解构的时候,模式和对象的 结构 是一样的
- 如果变量名冲突,可以使用 原变量: 新变量 的语法处理
代码演示:
<body>
<script>
let person = {
pname: '小明',
page: 20,
dog: {
name: '小黑',
age: 4,
child: {
name: '小白',
age: 1
}
}
}
let {
pname,
page,
dog: {
name: dname,
age: dage,
child: {
name, age
}
}
} = person;
console.log(pname, dname, name);
</script>
</body>
2.3 函数
2.3.1 箭头函数
ES6 中允许使用箭头定义函数 (=> goes to),目的是简化函数的定义并且里面的this也比较特殊。
回顾之前的函数定义
<body>
<script>
// 回顾,之前函数是如何定义的
function abc () {
// 函数体
// 可以有return
}
var fn = new Function('x', 'y', 'return x+y');
var fn = function (x, y) {
return x + y;
}
// 自调用函数,为什么要加小括号。
// 原因是一条语句,不能以function开头
// 自调用函数,前面可以加 ~ ! + ,而不使用小括号
~function (x, y) {
console.log(x + y);
}(2, 3);
var fn = function (x, y) {
console.log(x + y);
}
fn(3, 4);
(function (x, y) {
console.log(x + y);
})(3, 4);
</script>
</body>
箭头函数的语法
() => {}
(参数列表) => {函数体}
注意事项:
1.() => 之间不能换行
2. => {} 之间可以换行
代码演示:
let fn = (x, y) => {
return x + y;
}
// 调用
console.log( fn(5, 8) );
箭头函数特点
- 形参只有一个,可以省略小括号
let fn = (x) => {
return x * 2;
}
// 等同于
let fn = x => {
return x * 2;
}
- 函数体只有一行代码,可以省略大括号,并且表示返回函数体的内容
let fn = (x, y) => {
return x + y;
}
// 等同于
let fn = (x, y) => x + y;
箭头函数的注意点:
如果函数体仅仅返回一个字面量对象的话。需要给字面量对象加一个小括号
let abc = () => {x: 1}
console.log( abc() );
箭头函数功能上的特点
- 特点1:箭头函数内部没有 arguments
let fn = () => {
console.log(arguments); // 报错,arguments is not defined
};
fn(1, 2);
- 特点2:箭头函数内部的 this 指向外部作用域中的 this ,或者可以认为箭头函数没有自己的 this
let fn = () => {
console.log(this); // 查找一个变量this、函数内部没有找到,根据作用域链,去函数的外部进行查找,找到的结果为window对象
}
fn();
特点2 代码演示:
var age = 123; // 这里用var声明变量,否则就不能通过 window.age 来调用了
let obj = {
age: 456,
fn: function () {
console.log(this.age);
},
fn2: () => {
console.log(this.age); // window.age
}
}
obj.fn(); // 456,因为函数中的this表示当前的调用者 obj
obj.fn2(); // 123, 箭头函数中没有this。所以会去外层查找this,所以this是window
- 特点3: 箭头函数不能作为构造函数
let abcd = () => {
this.age = 20;
this.name = 'zs';
}
let p = new abcd(); // 报错,abcd is not a constructor
2.3.2 参数的默认值
ES6 之前函数不能设置参数的默认值
代码演示:
// ES5 中给参数设置默认值的变通做法
function fn(x, y) {
y = y || 'world';
console.log(x, y);
}
fn(1)
// ES6 中给函数设置默认值
function fn(x, y = 'world') {
console.log(x, y);
}
fn(2)
fn(2,3)
- 我们实际传递给函数的实参,优先级大于默认值。如果我们调用的时候,没有传递实参,则会使用默认值;如果调用函数的时候,传递了实参,则使用传递的实参
- 有默认值的参数,应该放到没有默认值的参数的后面。这样才会有意义
- 任何函数,都可以为参数设置默认值
2.3.3 rest 参数
rest 参数:剩余参数,以 … 修饰最后一个参数,把多余的参数都放到一个数组中。可以替代 arguments 的使用
- 接收剩余参数
let abc = (a, b, ...c) => {
console.log(a, b);
console.log(c);
}
abc(1, 2, 3, 4, 5);
- 直接使用剩余参数,一次性接收到所有的实参。解决了箭头函数内部不能使用arguments的问题
let abc = (...val) => {
console.log(val);
}
abc(3, 4, 5, 6, 7);
- 注意:因为要求剩余参数,必须是参数列表中的最后一个参数
3. 内置对象的扩展
- Array 的扩展
- String 的扩展
- Number 的扩展
- Set
3.1 Array 的扩展
-
扩展运算符
-
官网:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Spread_syntax
-
可以在函数调用/数组构造时, 将数组表达式或者string在语法层面展开;
-
可以在函数调用时, 将数组表达式在语法层面展开
let abc = (a, b, c) => { console.log(a, b, c); }
-
调用函数时,数组类型的实参,可以使用 … 展开
abc(...[1, 2, 3 ,4, 5]); // ======= abc(1, 2, 3, 4, 5);
-
可以在函数调用时, 将string在语法层面展开
let abc = (a, b, c) => { console.log(a, b, c) }; abc(...'hello'); // ===== abc('h', 'e', 'l', 'l', 'o');
-
可以在数组构造时, 将数组表达式在语法层面展开
let arr1 = [1, 2, 3]; let arr2 = [4, 5, 6]; let arr3 = [...arr1, ...arr2, 7, 8]; console.log(arr3);
-
可以在数组构造时, 将string在语法层面展开
let arr4 = [...'hello']; // 将字符串转成数组 console.log(arr4);
-
-
还可以在构造字面量对象时, 将对象表达式按key-value的方式展开
// ------ 什么样的对象,能够使用 ... // ------ - 只有能够遍历的对象,才能使用 ... let obj1 = { id: 1, name: 'zs' }; let obj2 = { nickname: 'lisi', age: 20 }; let fn = function () { this.sayHi = function () { console.log('sayHi'); }; this.height = '178cm'; } let obj4 = new fn(); let obj3 = {...obj1, ...obj2, ...obj4}; console.log(obj3);
-
-
Array.from()
- 把伪数组转换成数组
- 伪数组必须有length属性,没有length将得到一个空数组
- 转换后的数组长度,是根据伪数组的length决定的
let fakeArr = {
0: 'a',
1: 'b',
2: 'c',
length: 3
};
let arr = Array.from(fakeArr);
console.log(arr); // ['a', 'b', 'c']
// 转数组的对象必须有length值,因为得到的数组的成员个数是length指定的个数
// 上例中,如果length为2,则得到的数组为 ['a', 'b']
为什么要把它转成数组呢?因为伪数组不能调用数组方法
let arr = Array.from(obj);
console.log(arr); // ["apple", "banana", "pear"]
- 数组实例的 includes()
- 判断数组是否包含某个值,返回 true / false
- 参数1,必须,表示查找的内容
- 参数2,可选,表示开始查找的位置,0表示开头的位置
- 语法:
[].includes(search, [position])
let arr = [1, 4, 3, 9];
console.log(arr.includes(4)); // true
console.log(arr.includes(4, 2)); // false, 从2的位置开始查,所以没有找到4
console.log(arr.includes(5)); // false
-
下面介绍一组语法比较类似的方法
- forEach() – 遍历数组
- filter() – 遍历数组,找到满足条件的单元
- find() – 遍历数组,遍历的过程中,找到满足条件的第一个单元
- findIndex() – 遍历数组,遍历的过程中,找到满足条件的第一个索引(下标)
- some() – 遍历数组的过程中,判断数组中是否有满足条件的单元
- every() – 遍历数组的过程中,判断数组中的单元是否全部满足条件
- map() – 遍历数组的过程中,使用一个函数来处理数组的每个单元
-
上述方法的语法:
[].method(function (item, i, self) {
// item表示数组的单元
// i 表示数组的下标
// self 表示当前的数组
});
let arr1 = [8, 2, 7, 10, 5, 9, 4];
filter() – 遍历数组,找到满足条件的单元
// 找到数组中大于5的所有单元
let arr2 = arr1.filter(function (item, i) {
// return 条件
return item > 5;
});
console.log(arr2);
find() – 遍历数组,遍历的过程中,找到满足条件的第一个单元
// 查找数组中小于5的第一个值(单元)
let result = arr1.find(item => item < 5);
let result = arr1.find(function (item, i) {
return item < 5;
});
console.log(result);
findIndex() – 遍历数组,遍历的过程中,找到满足条件的第一个下标
let result = arr1.findIndex(function (item, i) {
return item < 5;
});
console.log(result);
some() – 遍历数组的过程中,判断数组中是否有满足条件的单元
// 判断一下,数组中是否有大于5的值,如果有,返回true;没有返回false
let result = arr1.some(function (item, i) {
// return '条件'
return item > 5
});
console.log(result);
every() – 遍历数组的过程中,判断数组中的单元是否全部满足条件
判断一下,数组中的值,是否全部大于5,如果是,返回true;如果不是,返回false
let result = arr1.every(function (item, i) {
// return '条件'
return item > 5
});
console.log(result);
map() – 遍历数组的过程中,使用一个函数来处理数组的每个单元
遍历数组,并且将数组中的每个单元的平方放到一个新数组中
let result = arr1.map(function (item) {
return item * item;
});
console.log(result);
console.log(arr1);
reduce() – 遍历数组的过程中,累加数组的每个单元
let result = [1, 2, 3, 4].reduce(function (a, b) {
// console.log(a); // a就是每次累加的结果
// console.log(b); // 代表数组中的每个单元
return a * b;
});
/*
起始值: 没有指定起始值,则 a 使用数组的第一个元素 ,所以 a = 1
第1次循环: a = 1, b = 2; 循环结束后, a = a + b; a = 3;
第2次循环: a = 3, b = 3; 循环结束后, a = a + b; a = 6;
第3次循环: a = 6; b = 4, 循环结束后, a = a + b; a = 10
*/
console.log(result);
// reduce() 的起始值是可以设定的
// [1, 2, 3, 4].reduce(fn, [起始值]);
// let result = [1, 2, 3, 4].reduce(function (a, b) {
// return a + b;
// }, 100);
// console.log(result);
3.2 String的扩展
- 模板字符串
- 模板字符串解决了字符串拼接不便的问题
- 模板字符串使用反引号 ` 括起来内容
- 模板字符串中的内容可以换行
- 变量在模板字符串中使用 ${name} 来表示,不用加 + 符号
let name = 'zs';
let age = 18;
// 拼接多个变量,在模板字符串中使用占位的方式,更易懂
let str = `我是${name},今年${age}`;
// 内容过多可以直接换行
let obj = {name: 'zhangsan', age: 20};
let arr = ['175cm', '60kg'];
let html = `
<div>
<ul>
<li>${obj.name}</li>
<li>${obj.age + 2}</li>
<li>${arr[0]}</li>
<li>${arr[1]}</li>
</ul>
</div>
`;
- includes(), startsWith(), endsWith()
- includes(str, [position]) 返回布尔值,表示是否找到了参数字符串
- startsWidth(str, [position]) 返回布尔值,表示参数字符串是否在原字符串的头部或指定位置
- endsWith(str, [length]) 返回布尔值,表示参数字符串是否在原字符串的尾部或指定位置。
console.log('hello world'.includes('e', 2)); // false 从位置2开始查找e,没有找到
console.log('hello world'.includes('e')); // true
console.log('hello world'.startsWith('h')); // 未指定位置,看开头是否是h,返回true
console.log('hello world'.startsWith('l', 2)); // 指定位置的字符是l,返回true
console.log('hello world'.endsWith('d')); // 未指定位置,结尾是d,返回true
console.log('hello world'.endsWith('r', 9)); // 先截取9个字符,然后看这9个字符串的结尾是不是r。返回true
- repeat()
repeat方法返回一个新字符串,表示将原字符串重复n次。
let html = '<li>itheima</li>';
html = html.repeat(10);
- trim()
trim() 方法可以去掉字符串两边的空白
console.log(' hello '.trim()); // hello
console.log(' hello '); //
3.3 Number的扩展
ES6 将全局方法parseInt()和parseFloat(),移植到Number对象上面,功能完全保持不变。
- Number.parseInt()
- Number.parseFloat()
console.log(parseInt('123abc'));
// ES6中,将parseInt移植到了Number对象上
console.log(Number.parseInt('123abc'));
3.4 Set
ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
- Set本身是一个构造函数,用来生成 Set 数据结构。
- Set的特点就是该对象里面的成员不会有重复。
// 1. 基本使用
let s = new Set();
// 得到一个空的Set对象
// 调用add方法,向s中添加几个值
s.add(3);
s.add(7);
s.add(9);
s.add(7); // Set对象中的成员都是唯一的,前面添加过7了,所以这里添加无效
console.log(s.size);
console.log(s); // {3, 7, 9}
- Set 的成员
- size:属性,获取 set 中成员的个数,相当于数组中的 length
- add(value):添加某个值,返回 Set 结构本身。
- delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
- has(value):返回一个布尔值,表示该值是否为Set的成员。
- clear():清除所有成员,没有返回值。
// 将一些重复的值加入到Set对象中,看看效果
const s = new Set();
// 使用forEach遍历前面的数组,然后将数组中的每个值都通过Set对象的add方法添加到Set对象中
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
// s = {2, 3, 5, 4}
// 遍历Set对象,发现重复的值只有一份
// for...in 循环中的 i 表示数组的下标,或对象的属性名
// for...of 循环中的 i 表示数组的值,或对象的值
for (let i of s) {
console.log(i);
}
// 2 3 5 4
另外初始化Set的时候,也可以为其传入数组或字符串,得到的Set对象中的成员不会有重复。
根据这个特点可以完成数组或字符串去重。
// Set 可以通过一个数组初始化
let set = new Set([1, 2, 1, 5, 1, 6]);
console.log(set); //Set(4) {1, 2, 5, 6}
// 数组去重
let arr = [...set]; // 方式一
console.log(Array.from(set)); // from是将伪数组变为数组;方式二
console.log(arr); // [1, 2, 5, 6]
// 完成字符串去重
let str = [...new Set('ababbc')].join('');
console.log(str); // abc
4. 定义对象的简洁方式
let id = 1;
let name = 'zs';
let age = 20;
// 之前定义对象的方案
// let obj = {
// // 属性: 值
// id: id,
// name: name,
// age: age,
// fn: function () {
// console.log(this.age);
// }
// };
// obj.fn();
// ES6的新方案
let obj = {
id, // 属性名id和前面的变量id名字相同,则可以省略 :id
name,
nianling: age,
// 下面的函数是上面函数的简化写法,可以省略 :function 。但是注意这里仅仅是上面函数的简化,不是箭头函数
fn () {
console.log(this.name);
}
};
obj.fn();
5. ECMAScript 6 降级处理 (保留)
5.1 ES 6 的兼容性问题
- ES6 虽好,但是有兼容性问题,IE7-IE11 基本不支持 ES6
ES6 兼容性列表 - 在最新的现代浏览器、移动端、Node.js 中都支持 ES6
- 后续我们会讲解如何处理 ES6 的兼容性
5.2 ES 6 降级处理
因为 ES 6 有浏览器兼容性问题,可以使用一些工具进行降级处理,例如:babel
-
降级处理 babel 的使用步骤
- 安装 Node.js
- 命令行中安装 babel
- 配置文件 .babelrc
- 运行
-
安装 Node.js
官网:https://nodejs.org/en/ -
项目初始化(项目文件夹不能有中文)
npm init -y
- 在命令行中,安装 babel babel官网
npm install @babel/core @babel/cli @babel/preset-env
- 配置文件 .babelrc (手工创建这个文件)
babel 的降级处理配置
{
"presets": ["@babel/preset-env"]
}
- 在命令行中,运行
# 把转换的结果输出到指定的文件
npx babel index.js -o test.js
# 把转换的结果输出到指定的目录
npx babel 包含有js的原目录 -d 转换后的新目录
参考:babel官网