let
命令,用来声明变量。它的用法相似于var
,可是所声明的变量,只在let
命令所在的代码块内有效。javascript
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined.
b // 1复制代码
const
声明一个只读的常量。一旦声明,常量的值就不能改变。
html
const PI = 3.1415;
PI // 3.1415
PI = 3;
// TypeError: Assignment to constant variable.复制代码
基本用法java
ES6容许按照必定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
git
本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。下面是一些使用嵌套数组进行解构的例子。
github
(1)数组的解构赋值正则表达式
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3
let [ , , third] = ["foo", "bar", "baz"];
third // "baz"
let [x, , y] = [1, 2, 3];
x // 1
y // 3
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]
let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []复制代码
(2)对象的解构赋值shell
对象的解构与数组有一个重要的不一样。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
编程
let { bar, foo } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
let { baz } = { foo: "aaa", bar: "bbb" };
baz // undefined复制代码
(3)字符串的解构赋值c#
字符串也能够解构赋值。这是由于此时,字符串被转换成了一个相似数组的对象
数组
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"复制代码
(4)数值和布尔值的解构赋值
解构赋值时,若是等号右边是数值和布尔值,则会先转为对象。
let {toString: s} = 123;
s === Number.prototype.toString // true
let {toString: s} = true;
s === Boolean.prototype.toString // true复制代码
(5)函数参数的解构赋值
[[1, 2], [3, 4]].map(([a, b]) => a + b);
// [ 3, 7 ]复制代码
function add([x, y]){
return x + y;
}
add([1, 2]); // 3复制代码
API:
0xFFFF
的字n
次。padStart
用于头部补全,padEnd
用于尾部补全。
(1)字符的Unicode表示法
只要将码点放入大括号,就能正确解读该字符。
"\u{20BB7}"
// "𠮷"
"\u{41}\u{42}\u{43}"
// "ABC"
let hello = 123;
hell\u{6F} // 123
'\u{1F680}' === '\uD83D\uDE80'
// true复制代码
(2)字符串的遍历器接口
ES6为字符串添加了遍历器接口,使得字符串能够被for...of
循环遍历。
for (let codePoint of 'foo') {
console.log(codePoint)
}
// "f"
// "o"
// "o"
复制代码
(3)模板字符串
实例代码以下,一看便明了:
// 普通字符串
`In JavaScript '\n' is a line-feed.`
// 多行字符串
`In JavaScript this is not legal.`
console.log(`string text line 1 string text line 2`);
// 字符串中嵌入变量
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`复制代码
(1)RegExp构造函数
RegExp构造函数第一个参数是一个正则对象,那么可使用第二个参数指定修饰符。并且,返回的正则表达式会忽略原有的正则表达式的修饰符,只使用新指定的修饰符。
new RegExp(/abc/ig, 'i').flags
// "i"复制代码
(2)字符串的正则方法
字符串对象共有4个方法,可使用正则表达式:match()
、replace()
、search()
和split()
。
ES6将这4个方法,在语言内部所有调用RegExp的实例方法,从而作到全部与正则相关的方法,全都定义在RegExp对象上。
String.prototype.match
调用 RegExp.prototype[Symbol.match]
String.prototype.replace
调用 RegExp.prototype[Symbol.replace]
String.prototype.search
调用 RegExp.prototype[Symbol.search]
String.prototype.split
调用 RegExp.prototype[Symbol.split]
(3)u修饰符
二进制和八进制表示法
ES6提供了二进制和八进制数值的新的写法,分别用前缀0b
(或0B
)和0o
(或0O
)表示。
API
Array.from()
用于将两类对象转为真正的数组:相似数组的对象(array-like object)和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)
Array.of()
Array.of
方法用于将一组值,转换为数组。
ES6容许为函数的参数设置默认值,即直接写在参数定义的后面。
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello复制代码
(1)箭头函数
ES6容许使用“箭头”(=>
)定义函数。
var f = v => v;
// 等于
var f = function(v) {
return v;
};
复制代码
(2)扩展运算符
扩展运算符(spread)是三个点(...)。它比如 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
console.log(...[1, 2, 3])
// 1 2 3
console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
[...document.querySelectorAll('div')]
// [<div>, <div>, <div>]复制代码
(1)属性和方法的简洁表示法
ES6容许直接写入变量和函数,做为对象的属性和方法。这样的书写更加简洁。
var foo = 'bar';
var baz = {foo};
baz // {foo: "bar"}
// 等同于
var baz = {foo: foo};复制代码
(2)方法的简洁表示
var o = {
method() {
return "Hello!";
}
};
// 等同于
var o = {
method: function() {
return "Hello!";
}
};复制代码
(3)Object.assign
Object.assign方法用于对象的合并,将源对象(source)的全部可枚举属性,复制到目标对象(target)。
var target = { a: 1 };
var source1 = { b: 2 };
var source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}复制代码
Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,若是源对象某个属性的值是对象,那么目标对象拷贝获得的是这个对象的引用。
Symbol值经过Symbol函数生成。这就是说,对象的属性名如今能够有两种类型,一种是原来就有的字符串,另外一种就是新增的Symbol类型。凡是属性名属于Symbol类型,就都是独一无二的,能够保证不会与其余属性名产生冲突。
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最先提出和实现,ES6将其写进了语言标准,统一了用法,原生提供了Promise对象。
所谓Promise,简单说就是一个容器,里面保存着某个将来才会结束的事件(一般是一个异步操做)的结果。从语法上说,Promise 是一个对象,从它能够获取异步操做的消息。Promise 提供统一的 API,各类异步操做均可以用一样的方法进行处理。
Promise对象有如下两个特色。
(1)对象的状态不受外界影响。Promise对象表明一个异步操做,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和Rejected(已失败)。只有异步操做的结果,能够决定当前是哪种状态,任何其余操做都没法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其余手段没法改变。
(2)一旦状态改变,就不会再变,任什么时候候均可以获得这个结果。Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变为Rejected。只要这两种状况发生,状态就凝固了,不会再变了,会一直保持这个结果。若是改变已经发生了,你再对Promise对象添加回调函数,也会当即获得这个结果。这与事件(Event)彻底不一样,事件的特色是,若是你错过了它,再去监听,是得不到结果的处。
基本用法
ES6规定,Promise对象是一个构造函数,用来生成Promise实例。
下面代码创造了一个Promise实例。
var promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操做成功 */){
resolve(value);
} else {
reject(error);
}
});复制代码
Promise实例生成之后,能够用then方法分别指定Resolved状态和Reject状态的回调函数。
promise.then(function(value) {
// success
}, function(error) {
// failure
});复制代码
下面是异步加载图片的例子。
function loadImageAsync(url) {
return new Promise(function(resolve, reject) {
var image = new Image();
image.onload = function() {
resolve(image);
};
image.onerror = function() {
reject(new Error('Could not load image at ' + url));
};
image.src = url;
});
}复制代码
Set基本用法
S6提供了新的数据结构Set。它相似于数组,可是成员的值都是惟一的,没有重复的值。
Set自己是一个构造函数,用来生成Set数据结构
var s = new Set();
[2, 3, 5, 4, 5, 2, 2].map(x => s.add(x));
for (let i of s) {
console.log(i);
}
// 2 3 5 4复制代码
Map结构的目的和基本用法
ES6提供了Map数据结构。它相似于对象,也是键值对的集合,可是“键”的范围不限于字符串,各类类型的值(包括对象)均可以看成键。也就是说,Object结构提供了“字符串—值”的对应,Map结构提供了“值—值”的对应,是一种更完善的Hash结构实现。若是你须要“键值对”的数据结构,Map比Object更合适。
var m = new Map();
var o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true
m.has(o) // false复制代码
Proxy
Proxy 用于修改某些操做的默认行为,等同于在语言层面作出修改,因此属于一种“元编程”(meta programming),即对编程语言进行编程。
Proxy 能够理解成,在目标对象以前架设一层“拦截”,外界对该对象的访问,都必须先经过这层拦截,所以提供了一种机制,能够对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操做,能够译为“代理器”。
Proxy
Reflect
对象与Proxy
对象同样,也是ES6为了操做对象而提供的新API。Reflect
对象的设计目的有这样几个。
(1) 将Object
对象的一些明显属于语言内部的方法(好比Object.defineProperty
),放到Reflect
对象上。现阶段,某些方法同时在Object
和Reflect
对象上部署,将来的新方法将只部署在Reflect
对象上。
(2) 修改某些Object方法的返回结果,让其变得更合理。好比,Object.defineProperty(obj, name, desc)
在没法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)
则会返回false
。
// 老写法
try {
Object.defineProperty(target, property, attributes);
// success
} catch (e) {
// failure
}
// 新写法
if (Reflect.defineProperty(target, property, attributes)) {
// success
} else {
// failure
}复制代码
terator(遍历器)的概念
JavaScript原有的表示“集合”的数据结构,主要是数组(Array)和对象(Object),ES6又添加了Map和Set。这样就有了四种数据集合,用户还能够组合使用它们,定义本身的数据结构,好比数组的成员是Map,Map的成员是对象。这样就须要一种统一的接口机制,来处理全部不一样的数据结构。
遍历器(Iterator)就是这样一种机制。它是一种接口,为各类不一样的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就能够完成遍历操做(即依次处理该数据结构的全部成员)。
Iterator的做用有三个:一是为各类数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员可以按某种次序排列;三是ES6创造了一种新的遍历命令for...of
循环,Iterator接口主要供for...of
消费。
Iterator的遍历过程是这样的。
(1)建立一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
(2)第一次调用指针对象的next
方法,能够将指针指向数据结构的第一个成员。
(3)第二次调用指针对象的next
方法,指针就指向数据结构的第二个成员。
(4)不断调用指针对象的next
方法,直到它指向数据结构的结束位置。
每一次调用next
方法,都会返回数据结构的当前成员的信息。具体来讲,就是返回一个包含value
和done
两个属性的对象。其中,value
属性是当前成员的值,done
属性是一个布尔值,表示遍历是否结束。
下面是一个模拟next
方法返回值的例子。
var it = makeIterator(['a', 'b']);
it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }
function makeIterator(array) {
var nextIndex = 0;
return {
next: function() {
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{value: undefined, done: true};
}
};
}复制代码
for...of循环
ES6 借鉴 C++、Java、C# 和 Python 语言,引入了for...of
循环,做为遍历全部数据结构的统一的方法。
一个数据结构只要部署了Symbol.iterator
属性,就被视为具备iterator接口,就能够用for...of
循环遍历它的成员。也就是说,for...of
循环内部调用的是数据结构的Symbol.iterator
方法。
for...of
循环可使用的范围包括数组、Set 和 Map 结构、某些相似数组的对象(好比arguments
对象、DOM NodeList 对象)、后文的 Generator 对象,以及字符串。
数组原生具有iterator
接口(即默认部署了Symbol.iterator
属性),for...of
循环本质上就是调用这个接口产生的遍历器,能够用下面的代码证实。
const arr = ['red', 'green', 'blue'];
for(let v of arr) {
console.log(v); // red green blue
}
const obj = {};
obj[Symbol.iterator] = arr[Symbol.iterator].bind(arr);
for(let v of obj) {
console.log(v); // red green blue
}复制代码
Generator函数是ES6提供的一种异步编程解决方案,语法行为与传统函数彻底不一样
Generator函数有多种理解角度。从语法上,首先能够把它理解成,Generator函数是一个状态机,封装了多个内部状态。
执行Generator函数会返回一个遍历器对象,也就是说,Generator函数除了状态机,仍是一个遍历器对象生成函数。返回的遍历器对象,能够依次遍历Generator函数内部的每个状态。
形式上,Generator函数是一个普通函数,可是有两个特征。一是,function
关键字与函数名之间有一个星号;二是,函数体内部使用yield
语句,定义不一样的内部状态(yield语句在英语里的意思就是“产出”)。
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();复制代码
上面代码定义了一个Generator函数helloWorldGenerator
,它内部有两个yield
语句“hello”和“world”,即该函数有三个状态:hello,world和return语句(结束执行)。
而后,Generator函数的调用方法与普通函数同样,也是在函数名后面加上一对圆括号。不一样的是,调用Generator函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是上一章介绍的遍历器对象(Iterator Object)。
下一步,必须调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用next
方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield
语句(或return
语句)为止。换言之,Generator函数是分段执行的,yield
语句是暂停执行的标记,而next
方法能够恢复执行。
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }复制代码
上面代码一共调用了四次next
方法。
第一次调用,Generator函数开始执行,直到遇到第一个yield
语句为止。next
方法返回一个对象,它的value
属性就是当前yield
语句的值hello,done
属性的值false,表示遍历尚未结束。
第二次调用,Generator函数从上次yield
语句停下的地方,一直执行到下一个yield
语句。next
方法返回的对象的value
属性就是当前yield
语句的值world,done
属性的值false,表示遍历尚未结束。
第三次调用,Generator函数从上次yield
语句停下的地方,一直执行到return
语句(若是没有return语句,就执行到函数结束)。next
方法返回的对象的value
属性,就是紧跟在return
语句后面的表达式的值(若是没有return
语句,则value
属性的值为undefined),done
属性的值true,表示遍历已经结束。
第四次调用,此时Generator函数已经运行完毕,next
方法返回对象的value
属性为undefined,done
属性为true。之后再调用next
方法,返回的都是这个值。
总结一下,调用Generator函数,返回一个遍历器对象,表明Generator函数的内部指针。之后,每次调用遍历器对象的next
方法,就会返回一个有着value
和done
两个属性的对象。value
属性表示当前的内部状态的值,是yield
语句后面那个表达式的值;done
属性是一个布尔值,表示是否遍历结束
yield语句
因为Generator函数返回的遍历器对象,只有调用next
方法才会遍历下一个内部状态,因此其实提供了一种能够暂停执行的函数。yield
语句就是暂停标志。
遍历器对象的next
方法的运行逻辑以下。
(1)遇到yield
语句,就暂停执行后面的操做,并将紧跟在yield
后面的那个表达式的值,做为返回的对象的value
属性值。
(2)下一次调用next
方法时,再继续往下执行,直到遇到下一个yield
语句。
(3)若是没有再遇到新的yield
语句,就一直运行到函数结束,直到return
语句为止,并将return
语句后面的表达式的值,做为返回的对象的value
属性值。
(4)若是该函数没有return
语句,则返回的对象的value
属性值为undefined
。
须要注意的是,yield
语句后面的表达式,只有当调用next
方法、内部指针指向该语句时才会执行,所以等于为JavaScript提供了手动的“惰性求值”(Lazy Evaluation)的语法功能。
function* gen() {
yield 123 + 456;
}复制代码
上面代码中,yield后面的表达式123 + 456
,不会当即求值,只会在next
方法将指针移到这一句时,才会求值。
yield
语句与return
语句既有类似之处,也有区别。类似之处在于,都能返回紧跟在语句后面的那个表达式的值。区别在于每次遇到yield
,函数暂停执行,下一次再从该位置继续向后执行,而return
语句不具有位置记忆的功能。一个函数里面,只能执行一次(或者说一个)return
语句,可是能够执行屡次(或者说多个)yield
语句。正常函数只能返回一个值,由于只能执行一次return
;Generator函数能够返回一系列的值,由于能够有任意多个yield
。从另外一个角度看,也能够说Generator生成了一系列的值,这也就是它的名称的来历(在英语中,generator这个词是“生成器”的意思)
ES7提供了async
函数,使得异步操做变得更加方便。async
函数是什么?一句话,async
函数就是Generator函数的语法糖。
依次读取两个文件。
var fs = require('fs');
var readFile = function (fileName) {
return new Promise(function (resolve, reject) {
fs.readFile(fileName, function(error, data) {
if (error) reject(error);
resolve(data);
});
});
};
var gen = function* (){
var f1 = yield readFile('/etc/fstab');
var f2 = yield readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};复制代码
写成async
函数,就是下面这样
var asyncReadFile = async function (){
var f1 = await readFile('/etc/fstab');
var f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
}复制代码
一比较就会发现,async
函数就是将Generator函数的星号(*
)替换成async
,将yield
替换成await
,仅此而已。
async
函数对 Generator 函数的改进,体如今如下四点
(1)内置执行器。Generator函数的执行必须靠执行器,因此才有了co
模块,而async
函数自带执行器。也就是说,async
函数的执行,与普通函数如出一辙,只要一行。
(2)更好的语义。async
和await
,比起星号和yield
,语义更清楚了。async
表示函数里有异步操做,await
表示紧跟在后面的表达式须要等待结果。
(3)更广的适用性。 co
模块约定,yield
命令后面只能是Thunk函数或Promise对象,而async
函数的await
命令后面,能够是Promise对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操做)。
(4)返回值是Promise。async
函数的返回值是Promise对象,这比Generator函数的返回值是Iterator对象方便多了。你能够用then
方法指定下一步的操做。
进一步说,async
函数彻底能够看做多个异步操做,包装成的一个Promise对象,而await
命令就是内部then
命令的语法糖。
对熟悉Java,object-c,c#等纯面向对象语言的开发者来讲,都会对class有一种特殊的情怀。ES6 引入了class(类),让JavaScript的面向对象编程变得更加简单和易于理解。
class Animal {
// 构造函数,实例化的时候将会被调用,若是不指定,那么会有一个不带参数的默认构造函数.
constructor(name,color) {
this.name = name;
this.color = color;
}
// toString 是原型对象上的属性
toString() {
console.log('name:' + this.name + ',color:' + this.color);
}
}
var animal = new Animal('dog','white');//实例化Animal
animal.toString();
console.log(animal.hasOwnProperty('name')); //true
console.log(animal.hasOwnProperty('toString')); // false
console.log(animal.__proto__.hasOwnProperty('toString')); // true
class Cat extends Animal {
constructor(action) {
// 子类必需要在constructor中指定super 函数,不然在新建实例的时候会报错.
// 若是没有置顶consructor,默认带super函数的constructor将会被添加、
super('cat','white');
this.action = action;
}
toString() {
console.log(super.toString());
}
}
var cat = new Cat('catch')
cat.toString();
// 实例cat 是 Cat 和 Animal 的实例,和Es5彻底一致。
console.log(cat instanceof Cat); // true
console.log(cat instanceof Animal); // true复制代码
ES6容许在一个模块中使用export来导出多个变量或函数。
导出变量/常量
//test.js
export var name = 'Rainbow'
复制代码
ES6将一个文件视为一个模块,上面的模块经过 export 向外输出了一个变量。一个模块也能够同时往外面输出多个变量。
//test.js
var name = 'Rainbow';
var age = '24';
export {name, age};
复制代码
导出函数
// myModule.js
export function myModule(someArg) {
return someArg;
}
复制代码
定义好模块的输出之后就能够在另一个模块经过import引用。
import {myModule} from 'myModule';// main.js
import {name,age} from 'test';// test.js
复制代码
修饰器(Decorator)是一个函数,用来修改类的行为。这是ES7的一个提案,目前Babel转码器已经支持。
修饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,修饰器能在编译阶段运行代码。
类的修饰
function testable(target) {
target.isTestable = true;
}
@testable
class MyTestableClass {}
console.log(MyTestableClass.isTestable) // true复制代码
上面代码中,@testable
就是一个修饰器。它修改了MyTestableClass
这个类的行为,为它加上了静态属性isTestable
。
基本上,修饰器的行为就是下面这样。
@decorator
class A {}
// 等同于
class A {}
A = decorator(A) || A;复制代码
方法的修饰
class Person {
@readonly
name() { return `${this.first} ${this.last}` }
}复制代码