ES6(ESMAScript 6, ESMAScript2015)
是指ESMA
组织在2015
年发布的ESMAScript 2015
标准,因为相比ES5
(2009年发布),ES6
的改动很是大,故广义上,咱们也将ES6
统称全部ES5
以后的新特性,包括ES7
(ESMAScript2016
),ES8
(ESMAScript2017
)...ES10
(ESMAScript2019
)等,这里专指ES6
。编程
var
,let
与const
咱们知道在ES5
中,用var
关键字声明的变量存在变量提高,函数及变量的声明都将被提高到函数的最顶部,这就致使能够在使用以后在声明变量,这种不规范的行为不少时候都依靠编程习惯来约束,尤为对于新手来讲存在很多隐患,因而let
与const
应运而生,两者的做用域都是当前代码块域,let
声明的变量能够修改,const
声明的是常量,声明后不可修改。数组
let foo = 10;
const bar = {key: 20};
foo = 20;
bar.key = 30;
bar = 30; // TypeError
复制代码
这里解释一下bar.key = 30
这句话改变的不是bar
的值,只是修改了其中的属性,就比如古代的女人嫁鸡随鸡、从一而终,后来这只鸡变了,那也只是变成了一只“大鸡”,但鸡仍是这只鸡……app
在JavaScript
中,咱们总能听到这样的话:在JavaScript
中只有函数做用域,没有块级做用域。那么他们究竟是什么呢?async
以一个function
声明的代码块是一个函数做用域,以下代码块中的scope 1
和scope 2
就是同一个函数做用域,但他们是两个不一样的代码块函数
function fn(){
console.log(foo1); // undefined
console.log(bar1); // undefined
// scope 1
var foo1 = 10;
let foo2 = 20;
if(true) {
// scope 2
var bar1 = 30;
let bar2 = 40;
}
console.log(foo1); // 10
console.log(foo2); // 20
console.log(bar1); // 30
console.log(bar2); // ReferenceError
}
复制代码
咱们能够看出用let
声明的变量bar2
在if
语句外是不能访问的(ReferenceError
引用错误,表明引用了一个未声明的变量)ui
在上面的例子中第一句console.log(bar1);
输出了undefined
,并未报错,说明用var
声明的变量在函数开始时就已经定义(即变量提高),但须要注意的是此时并未初始化。但采用let
和const
声明的变量则不存在变量提高,必须先声明后使用,不然就会报错:ReferenceError
,同时咱们把这种在声明以前不可以使用这种特性成为 暂时性死区(Temproal Dead Zone, TDZ)。this
console.log(foo); // ReferenceError
let foo = 10;
复制代码
var foo = 10;
var foo = 20;
let bar = 20;
let bar = 20; // SyntaxError
复制代码
当咱们用var
声明变量的时候,是能够重复声明的,后声明的会覆盖前面的声明,但当咱们用let
或const
声明的时候则会报语法错误SyntaxError
,注意,function
声明的函数也和var
有类似的表现:存在 变量提高 和 重复声明。spa
解构顾名思义按照必定的结构“解析”一个对象,经过这种方式咱们能够从数组或对象中取值,本质上属于 模式匹配,这是ES6
给咱们提供的新语法,只要等号两边的模式相同便可解析相应的值。prototype
// 解构一个对象,能够只解构咱们须要的部分
const {foo, bar} = {foo: 1, bar: 2, other: 3};
console.log(foo, bar); // 1 2
// 解构一个数组
const [first, second] = [10, 20, 30];
console.log(first, second); // 10 20
// 忽略数组的某个值
const [first, , third] = [10, 20, 30];
console.log(first, third); // 10 30
// 解构一个不存在的键时,其值为 undefined
const {foo, bar} = {foo: 1}; // undefined
// 注意数组的 length
const [length] = [12];
console.log(length); // 12
// 用方括号可花括号解析的差异
const {length} = [12];
console.log(length); // 1
// 当属性中包含关键字或保留字时须要重命名
const {class: clazz} = {class: 'class-name'};
// 动态匹配,一样须要重命名
const key = 'thisKey';
const {[key]: value} = {thisKey: 23};
console.log(value); // 23
// 一样动态匹配也可用在对象声明中
const key = 'currentKey';
const obj = {[key]: 20};
console.log(obj); // {currentKey: 20}
// 用解构赋值交换两个变量,不须要声明第三个变量
let foo = 10;
let bar = 20;
console.log(foo, bar); // 10 20
[foo, bar] = [bar, foo];
console.log(foo, bar); // 20 10
复制代码
在使用解构赋值的时候,可使用扩展运算符...
来将剩余值统一放入一个对象中,rest
// 用于对象
const {foo, ...rest} = {foo: 1, bar: 2, other: 3};
console.log(foo, rest); // 1 {bar: 2, other: 3}
// 用于数组
const [first, ...others] = [1, 2, 3];
console.log(first, others); // 1 [2, 3]
// 函数调用中传递参数
function callback(first, second){
console.log(first, second);
}
let params = [1, 2];
callback(...params); // 输出 1 2
// 用于获取 arguments,这种特性主要用于箭头函数
const arrow = (...args) => {
console.log(args);
};
arrow(1, 2, 3); // 输出数组 [1, 2, 3]
复制代码
扩展运算符其实是调用了Iterator
接口,一般的Array
,String
,NodeList
,Arguments
,Set
,Map
都实现了这个接口,因此可经过扩展运算符得到arguments
,也可用于函数调用时传递参数,但Object
并未实现Iterator
,因此当咱们调用fn(...obj)
时,会报错TypeError
。
在咱们使用解构赋值的时候,能够为不存在的值或值为undefined
赋默认值
// 不存在指定映射
const {foo, bar = 20} = {foo: 10};
console.log(foo, bar); // 10 20
// 不存在指定值为 undefined
const {foo, bar = 20} = {foo: 10, bar: undefined};
console.log(foo, bar); // 10 20
复制代码
只有为undefined
时才会执行默认值赋值步骤
const {foo, bar = (function(){
console.log('默认值');
return 20;
})()} = {foo: 10};
// 默认值
console.log(foo, bar); // 10 20
function defaultVal(){
console.log('默认值');
return 20;
}
const {key1, key2 = defaultVal()} = {key1: 10, key2: null};
console.log(key1, key2); // 10 null
// 可见 key2 为 null,并未输出 “默认值”
复制代码
在声明函数时,咱们也能够为函数参数赋默认值,一样只有在传入参数为undefined
时,才执行默认值赋值操做
function fn(foo, bar = 20){
console.log(foo, bar);
}
fn(10); // 10 20
复制代码
ES6
给咱们带来了新的函数语法箭头函数
,在日常的书写中,极大的提升了咱们的开发效率,可是箭头函数和普通函数却有着很大的差异
// 基本使用
const arrow = () => {};
// 有一个参数
const arrow = (arg) => {};
const arrow = arg => {}; // 括号可省略
// 有多个参数
const arrow = (arg1, arg2, arg3) => {}
// 函数体只有一句
const arrow = () => 1; // 花括号可省略
// 等价于
const arrow = () => {return 1;};
// 函数体有多句
const arrow = arg => {
if(arg){
return 1;
} else {
return 2;
}
};
// 返回一个空对象
// 这实际上只有一句,但括号不能省略,不然会被解析成代码块
const arrow = () => ({});
复制代码
this
箭头函数的this
是和声明时的父做用域绑定的,不可经过其余方式(call
,apply
,bind
)更改this
指向,如:
'use strict';
function fn1(){
console.log(this.foo);
return function() {
console.log(this.foo)
}
}
const obj = {foo: 10};
const bar = fn1.call(obj); // 10
bar(); // TypeError(由于此时 this 指向 undefined)
const changedObj = {foo: 60};
bar.call(changedObj); // 60 (this 能够从新绑定)
复制代码
若是是箭头函数:
'use strict';
function fn1(){
console.log(this.foo);
return () => {
console.log(this.foo)
}
}
const obj = {foo: 10};
const bar = fn1.call(obj); // 10
bar(); // 10 (此时 this 仍然指向 obj)
obj.foo = 20;
bar(); // 20
const changedObj = {foo: 60};
bar.call(changedObj); // 20
复制代码
new
const foo = () => {};
const bar = function(){
}
new bar(); // bar {}
new foo(); // TypeError
复制代码
arguments
箭头函数没有arguments
,但可使用上面的解构语法得到 参数列表,但获得的不是Arguments
对象,而是一个包含全部参数的数组
const arrow = (...args) => {
console.log(args);
}
arrow(1, 2, 3); // [1, 2, 3]
复制代码
class
语法在ES5
中,只能经过Function.prototype
来实现类的声明和继承,ES6
提供了新的语法来声明一个类,使其更加贴合面向对象编程,
// ES5
function Student(){}
Student.staticMethod = function(){
// ES5 的静态方法
}
Student.prototype.memberMethod = function(){
// ES5 的实例方法
}
// ES6
class Person {
constructor(name) {
this.name = name;
this.memberMethod = this.memberMethod.bind(this);
}
static staticMethod(){
console.log('这是一个静态方法');
}
static memberMethod(){
// 注意实例方法 ES6 并未绑定 this
// React 中也建议了,在构造器中应手动绑定 this
// 如上
console.log('这是实例方法');
}
}
复制代码
(相比以上,这是大概是最复杂的,下一篇将专门将这一特性,后续还会介绍ES7
的async
,await
,他们都是依赖Promise
,因此……很重要!)