#1. 块级作用域(let)
ES5 只有全局作用域和函数作用域(例如,我们必须将代码包在函数内来限制作用域),这导致很多问题:
情况 1:内层变量覆盖外层变量
var tmp = new Date();
function f() {
console.log(tmp); //undefined
if (false) {
var tmp = "hello world";
}
}
情况 2:变量泄露,成为全局变量
var s = 'hello';
for (var i = 0; i < s.length; i++) {
console.log(s[i]);
}
console.log(i); // 5
ES6 提供 let 和 const 来代替 var 声明变量,新的声明方式支持用大括号表示的块级作用域,这会带来一些好处:
1.不再需要立即执行的函数表达式(IIFE)
在 ES5 中,我们需要构造一个立即执行的函数表达式去保证我们不污染全局作用域。在 ES6 中, 我们可以使用更简单的大括号({}),然后使用 const 或者 let 代替 var 来达到同样的效果。
2.循环体中的闭包不再有问题
在 ES5 中,如果循环体内有产生一个闭包,访问闭包外的变量,会产生问题。在 ES6,你可以使用 “let” 来避免问题。
3.防止重复声明变量
ES6 不允许在同一个作用域内用 let 或 const 重复声明同名变量。这对于防止在不同的 js 库中存在重复声明的函数表达式十分有帮助。
#2. 箭头函数
- 缩减代码
- 改变 this 指向
使用注意点
(1)函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用 new 命令,否则会抛出一个错误。
(3)不可以使用 arguments 对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
(4)不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。
#3. 模板字符串
模板字符串是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。 模板字符串中嵌入变量和函数,需要将变量名写在${}之中。
#4. 数组扩展
- Array.from() : 将伪数组对象或可遍历对象转换为真数组
- Array.of(v1, v2, v3) : 将一系列值转换成数组
- 数组实例的 find() 和 findIndex()
- 数组实例的 includes()
- 数组实例的 entries(),keys() 和 values()
#5. rest 参数
ES6 引入 rest 参数(形式为…变量名),用于获取函数的多余参数,这样就不需要使用 arguments 对象了。
rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
我们举个例子:如何实现一个求和函数?
传统写法:
function addNumbers(a,b,c,d,e){
var numbers = [a,b,c,d,e];
return numbers.reduce((sum,number) => {
return sum + number;
},0)
}
console.log(addNumbers(1,2,3,4,5));//15
ES6 写法:
function addNumbers(...numbers){
return numbers.reduce((sum,number) => {
return sum + number;
},0)
}
console.log(addNumbers(1,2,3,4,5));//15
也可以与解构赋值组合使用
var array = [1,2,3,4,5,6];
var [a,b,...c] = array;
console.log(a);//1
console.log(b);//2
console.log(c);//[3, 4, 5, 6]
rest 参数还可以与箭头函数结合
const numbers = (...nums) => nums;
numbers(1, 2, 3, 4, 5)// [1,2,3,4,5]
注意:
1.每个函数最多只能声明一个 rest 参数,而且 rest 参数必须是最后一个参数,否则报错。
2.rest 参数不能用于对象字面量 setter 之中
#6. 展开运算符
与剩余参数关联最密切的就是扩展运算符。剩余参数允许你把多个独立的参数合并到一个数组中;而扩展运算符则允许将一个数组分割,并将各个项作为分离的参数传给函数。
当用在字符串或数组前面时称为扩展运算符,个人觉得可以理解为 rest 参数的逆运算,用于将数组或字符串进行拆解。有些时候,函数不允许传入数组,此时使用展开运算符就很方便,不信的话,咱们看个例子:Math.max()方法,它接受任意数量的参数,并会返回其中的最大值。
let value1 = 25,
let value2 = 50;
console.log(Math.max(value1, value2)); // 50
但若想处理数组中的值,此时该如何找到最大值?Math.max()方法并不允许你传入一个数组。其实你可以像使用 rest 参数那样在该数组前添加…,并直接将其传递给 Math.max()
let values = [25,50,75, 100]
//等价于console.log(Math.max(25,50,75,100));
console.log(Math.max(...values)); //100
扩展运算符还可以与其他参数混用
let values = [-25,-50,-75,-100]
console.log(Math.max(...values,0)); //0
扩展运算符拆解字符串与数组
var array = [1,2,3,4,5];
console.log(...array);//1 2 3 4 5
var str = "String";
console.log(...str); // S t r i n g
还可以实现拼接
var defaultColors = ["red","greed"];
var favoriteColors = ["orange","yellow"];
var fallColors = ["fire red","fall orange"];
console.log(["blue","green",...fallColors,...defaultColors,...favoriteColors]
//["blue", "green", "fire red", "fall orange", "red", "greed", "orange", "yellow"]
#7. 解构赋值
#8. Set和Map
set:它类似于数组,但是成员的值都是唯一的,没有重复的值。
map:JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。
#9. Promise与async/await
在 JavaScript 的世界中,所有代码都是单线程执行的。由于这个“缺陷”,导致 JavaScript 的所有网络操作,浏览器事件,都必须是异步执行。Promise 是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理和更强大。
promise原理:
一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 pending 变为 fulfilled 和从 pending 变为 rejected。promise 对象初始化状态为 pending ;当调用 resolve(成功),会由 pending => fulfilled ;当调用 reject(失败),会由 pending => rejected。具体流程见下图:
Promise 的使用流程
- new Promise 一个实例,而且要 return
- new Promise 时要传入函数,函数有 resolve reject 两个参数
- 成功时执行 resolve,失败时执行 reject
- then 监听结果
function loadImg(src){
const promise=new Promise(function(resolve,reject){
var img=document.createElement('img')
img.onload=function(){
resolve(img)
}
img.onerror=function(){
reject()
}
img.src=src
})
return promise//返回一个promise实例
}
var src="http://www.imooc.com/static/img/index/logo_new.png"
var result=loadImg(src)
result.then(function(img){
console.log(img.width)//resolved(成功)时候的回调函数
},function(){
console.log("failed")//rejected(失败)时候的回调函数
})
result.then(function(img){
console.log(img.height)
})
promise 会让代码变得更容易维护,像写同步代码一样写异步代码,同时业务逻辑也更易懂。
#10. 类(class)
看下面例子:
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
等同于:
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype = {
constructor() {},
toString() {}
};
Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
class ColorPoint extends Point {
}
ES5实现继承与ES6的区别:
ES5的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))
ES6的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(必须先调用super方法),然后用子类的构造函数修改this
#11. 模块化(Module)
看下面例子:
// main.js
import { firstName , lastName , year } from 'profile.js';
// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export {firstName, lastName, year};
参考地址
以上是ES6的主要核心的内容以及最常用的简介,具体使用内容参考下面链接内容: