let & const
模版字段量
解构
对象字面量简写
1 | let type = 'quartz'; |
for… of 循环
展开运算符 ...
...
表示剩余参数
可变参数函数1
2
3
4
5
6
7function sum(...nums) {
let total = 0;
for(const num of nums) {
total += num;
}
return total;
}
箭头函数 =>
赋值变量
const greet = name =>
Hello ${name}!`;如果列表中有两个或多个参数,或者有零个,则需要将参数列表放在圆括号内:
1
2
3// empty parameter list requires parentheses
const sayHi = () => console.log('Hello Udacity Student!');
sayHi();我们看过的所有箭头函数都只有一个表达式作为函数主体:
1 | const upperizedNames = ['Farrin', 'Kagure', 'Asser'].map( |
这种函数主体形式称为”简写主体语法”。简写语法:
在函数主体周围没有花括号
自动返回表达式。
如果箭头函数的主体内需要多行代码,则可以使用”常规主体语法”。
1 | const upperizedNames = ['Farrin', 'Kagure', 'Asser'].map( name => { |
- this和箭头函数
对于箭头函数,this 的值基于函数周围的上下文。换句话说,箭头函数内的,this 的值与函数外面的 this 的值一样。
默认函数参数
1 | function createSundae({scoops = 1, toppings = ['Hot Fudge']} = {}) { |
javascript中的类概念
javascript并不是一门基于类的语言,它还是会使用函数来创建对象,并通过原型将它们关联在一起.class,extend等关键字只是语法糖
ES5创建1
2
3
4
5
6
7
8
9
10
11
12
13function Plane(numEngines) {
this.numEngines = numEngines;
this.enginesActive = false;
}
// methods "inherited" by all instances
Plane.prototype.startEngines = function () {
console.log('starting engines...');
this.enginesActive = true;
};
const richardsPlane = new Plane(1);
richardsPlane.startEngines();
ES6创建1
2
3
4
5
6
7
8
9
10
11class Plane {
constructor(numEngines) {
this.numEngines = numEngines;
this.enginesActive = false;
}
startEngines() {
console.log('starting engines…');
this.enginesActive = true;
}
}
注意:类中的方法定义之间没有任何逗号了?在类中,不用逗号来区分属性或方法。如果添加逗号,将出现 SyntaxError:unexpected token
ES6 中的子类
现在使用新的 super 和 extends 关键字扩展类。
1 | class Maple extends Tree { |
Maple 类是 Tree 的子类,并使用关键字 extends 将自己设为子类。要让子类可以访问到父类,需要使用关键字 super。注意到 super 有两种使用方式吗?在 Maple 的构造方法中,super 被用作函数。在 Maple 的changeSeason() 方法中,super 被用作对象!
- 使用子类
像大多数新增加的特性,使用 class、super 和 extends 创建子类时设置代码少了很多,语法更清晰。
只需记住,在底层,函数和原型之间的连接是一样的。
super 必须在 this 之前被调用
在子类构造函数中,在使用 this 之前,必须先调用超级类。
ES6的内置对象
javascript原始类型:numbers,strings,bool,null,undefined
新增原始类型 symbol:唯一标识符,用于唯一标识对象中的属性
1 | const sym1 = Symbol('apple'); |
1 | const bowl = { |
output: Object {Symbol(apple): Object, Symbol(banana): Object,
Symbol(orange): Object, Symbol(banana): Object}
通过更改 bowl 的属性并使用标识符,每个属性都是唯一的标识符,第一个香蕉不会被第二个香蕉覆盖。
迭代器
迭代器方法(可通过常量 [Symbol.iterator] 获得)是一个无参数的函数,返回的是迭代器对象。迭代器对象是遵守迭代器协议的对象。
迭代器协议
迭代器协议用来定义对象生成一系列值的标准方式。实际上就是现在有了定义对象如何迭代的流程。通过执行 .next() 方法来完成这一流程。
1 | const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; |
Object.keys(对象) 返回对象的属性到一个数组
1 | const james = { |
创建一个对象的迭代器接口
1 | const james = { |
Set(集合)
可以通过几种不同的方式创建 Set。第一种很简单:1
2const games = new Set();
console.log(games);
根据值列表创建 Set,则使用数组1
2const games = new Set(['Super Mario Bros.', 'Banjo-Kazooie', 'Mario Kart', 'Super Mario Bros.']);
console.log(games);
修改 Set。 使用 .add()
和 .delete()
方法。.clear()
方法清除所有条目.
使用 Set。 使用 .size()
来获取Set长度。使用.has()
来检查某个条目。
使用 .values()
方法可以返回 Set 中的值。.values()
方法的返回值是 SetIterator
对象。.keys()
方法将和 .values()
方法的行为完全一样:将 Set 的值返回到新的迭代器对象中。.keys()
方法是 .values()
方法的别名,和 Map(映射)中的类似。
SetIterator
该迭代器对象存储在变量中,并使用 .next()
访问 Set 中的每一项。
1 | const iterator = months.values(); |
使用 for…of 循环
一种更简单的方法去循环访问 Set 中的项目是 for…of 循环。
WeakSet(弱集合)
WeakSet 只能包含对象
WeakSet 无法迭代,意味着不能循环访问其中的对象
WeakSet 没有 .clear()
方法
1 | const student1 = { name: 'James', age: 26, gender: 'male' }; |
Map
创建Mapconst employees = new Map();
修改 Map
和 Set 不同,你无法使用值列表创建 Map;而是使用 Map 的 .set() 方法添加键值。
1 | const employees = new Map(); |
要移除键值对,只需使用 .delete()
方法。
和 Set 类似,你可以使用 .clear()
方法从 Map 中删除所有键值对。
构建 Map 后,可以使用 .has()
方法并向其传入一个键来检查 Map 中是否存在该键值对。
还可以通过向.get()
方法传入一个键,检索 Map 中的值。
循环访问 Map
你已经创建了 Map,添加了一些键值对,现在你想循环访问该 Map。幸运的是,可以通过以下三种方式循环访问:
1)使用 Map 的默认迭代器循环访问每个键或值(.keys()
&.values()
)
2)使用新的 for...of
循环来循环访问每个键值对
在对 Map 使用 for…of 循环时,并不会得到一个键值或一个值。键值对会拆分为一个数组,第一个元素是键,第二个元素是值
1 | const members = new Map(); |
3)使用 Map 的 .forEach()
方法循环访问每个键值对1
members.forEach((value, key) => console.log(value, key));
WeakMap
WeakMap和WeakSet不会阻止对象被当作垃圾回收。WeakMap 和普通 Map 很像,但是具有以下关键区别:
WeakMap 只能包含对象作为键,
WeakMap 无法迭代,意味着无法循环访问,并且
WeakMap 没有 .clear() 方法。
和 WeakSet 相似,WeakMap 利用垃圾回收机制让其可以更简单地使用和易维护。
垃圾回收
在 JavaScript 中,创建新的值时会分配内存,并且当这些值不再需要时,将自动释放内存。这种内存不再需要后释放内存的过程称为垃圾回收。
WeakMap 通过专门处理对象作为键来利用这一点。如果将对象设为 null,则本质上是删除该对象。当 JavaScript 的垃圾回收器运行时,该对象之前占用的内存将被释放,以便稍后在程序中使用。
这种机制的好处在于你不用去担心要删掉对 WeakMap 中已删除对象的引用键,JavaScript 会帮你删除!如果对象被删除,当垃圾回收器运行时,该对象键也会从弱映射中删除。这样的话,如果你想要一种高效、轻便的解决方法去创建一组具有元数据的对象,就可以使用 WeakMap。
Promise
JavaScript Promise是用新的构造函数 new Promise() 创建而成的。promise 使你能够展开一些可以异步完成的工作,并回到常规工作。创建 promise 时,必须向其提供异步运行的代码。
完成这一切后,JavaScript 如何通知我们它已经完成操作,准备好让我们恢复工作?它通过向初始函数中传入两个函数来实现这一点,通常我们将这两个函数称为 resolve 和 reject。1
2
3
4
5
6
7
8
9
10
11
12
13new Promise(function (resolve, reject) {
window.setTimeout(function createSundae(flavor = 'chocolate') {
const sundae = {};
// request ice cream
// get cone
// warm up ice cream scoop
// scoop generous portion into cone!
if ( /* iceCreamConeIsEmpty(flavor) */ ) {
reject(`Sorry, we're out of that flavor :-(`);
}
resolve(sundae);
}, Math.random() * 2000);
});
当 sundae 被成功创建后,它会调用 resolve 方法并向其传递我们要返回的数据,在本例中,返回的数据是完成的 sundae。因此 resolve 方法用来表示请求已完成,并且成功完成了请求。
如果请求存在问题,无法完成请求,那么我们可以使用传递给该函数的第二个函数。通常,该函数存储在一个叫做”reject”的标识符中,表示如果请求因为某种原因失败了,应该使用该函数。
Promise 立即返回对象
首先要注意的是,Promise 将立即返回一个对象。
1 | const myPromiseObj = new Promise(function (resolve, reject) { |
该对象上具有一个 .then()
方法,我们可以让该方法通知我们 promise 中的请求成功与否。.then()
方法会接收两个函数:
请求成功完成时要运行的函数
请求失败时要运行的函数
1 | mySundae.then(function(sundae) { |
可以看出,传递给 .then()
的第一个函数将被调用,并传入 Promise 的 resolve
函数需要使用的数据。这里,该函数将接收 sundae
对象。第二个函数传入的数据会在 Promise 的 reject 函数被调用时使用。这里,该函数收到错误消息”Sorry, we’re out of that flavor :-(“,在上述 Promise 代码中,reject
函数被调用。
Proxy
Proxy 内的一个传递
创建 Proxy 的最简单方式是提供对象和空的 handler(处理器)对象。
1 | var richard = {status: 'looking for work'}; |
Get Trap(捕获器)
get 用来截获对属性的调用:
1 | const richard = {status: 'looking for work'}; |
在上述代码中,handler 对象具有一个 get 方法(因为被用在 Proxy 中,所以将”function”(方法)称之为”trap”(捕获器))。当代码 agent.status; 在最后一行运行时,因为存在 get 捕获器,它将截获该调用以获得 status(状态)属性并运行 get 捕获器方法。这样将会输出 Proxy 的目标对象(richard 对象),然后输出被请求的属性(status 属性)的名称。它的作用就是这些!它不会实际地输出属性!这很重要 —— 如果使用了捕获器,你需要确保为该捕获器提供所有的功能。
因此每当 Proxy 上的属性被访问,get
trap 将接管任务。如果我们想截获调用以更改属性,则需要使用 set
trap!set
trap 用来截获将更改属性的代码。set
trap 将接收:它代理的对象被设置的属性 Proxy 的新值
1 | const richard = {status: 'looking for work'}; |
在上述代码中,注意 set trap 会检查是否设置了 payRate 属性。如果设置了,Proxy 就从中拿走 15% 的费用作为自己的佣金!当演员的薪酬是一千美元时,因为 payRate 属性已设置,代码从中扣除 15% 的费用,并将实际 payRate 属性设为 850;
1 | const proxyObj = new Proxy({age: 5, height: 4}, { |
就像 ES5 代码那样一切正常,但是当我们添加新的属性时,看看会发生什么:1
2proxyObj.weight = 120; // set a new property on the object
proxyObj.weight; // logs 'getting the weight property' & 120
看到了吗?向 proxy 对象中添加了 weight 属性,稍后检索它时,它显示了一条日志消息!
因此 proxy 对象的某些功能可能看起来类似于现有的 ES5 getter/setter 方法,但是对于 proxy,在初始化对象时,不需要针对每个属性使用 getter/setter 初始化对象。
Proxy 对象介于真正的对象和调用代码之间。调用代码与 Proxy 交互,而不是真正的对象。要创建 Proxy:
使用 new Proxy() 构造函数
将被代理的对象传入为第一项
第二个对象是 handler(处理器)对象
handler 对象由 13 种不同的 trap 之一构成
trap 是一种函数,将截获对属相的调用,让你运行代码
如果未定义 trap,默认行为会被发送给目标对象
Proxy 是一种强大的创建和管理对象之间的交互的新方式。
生成器
可暂停的函数。(有效的生成器*
号可放置于function和函数名之间的任何位置)
如果我们希望能够中途暂停运行函数,则需要使用 ES6 中新提供的一种函数,叫做 generator(生成器)函数!我们来看一个示例:1
2
3
4
5
6
7
8function* getEmployee() {
console.log('the function has started');
const names = ['Amanda', 'Diego', 'Farrin', 'James', 'Kagure', 'Kavita', 'Orit', 'Richard'];
for (const name of names) {
console.log( name );
}
console.log('the function has ended');
}
现在看看当我们尝试运行该函数时,会发生什么:1
2
3getEmployee();
// this is the response I get in Chrome:
getEmployee {[[GeneratorStatus]]: "suspended", [[GeneratorReceiver]]: Window}
生成器被调用时,它不会运行函数中的任何代码,而是创建和返回迭代器。该迭代器可以用来运行实际生成器的内部代码。1
2const generatorIterator = getEmployee();
generatorIterator.next();
产生我们期望的代码:1
2
3
4
5
6
7
8
9
10the function has started
Amanda
Diego
Farrin
James
Kagure
Kavita
Orit
Richard
the function has ended
关键字 yield
关键字 yield
是 ES6 中新出现的关键字。只能用在生成器函数中。yield 会导致生成器暂停下来。我们向我们的生成器中添加 yield
,试试看:
1 | function* getEmployee() { |
如果修改成yield name
暂停后返回外部调用如下:1
2
3
4
5
6const generatorIterator = getEmployee();
let result = generatorIterator.next();
result.value // is "Amanda"
generatorIterator.next().value // is "Diego"
generatorIterator.next().value // is "Farrin"
我们还可以使用关键字 yield
从生成器中获取数据。我们还可以将数据发送回生成器中。方式是使用 .next()
方法:1
2
3
4function* displayResponse() {
const response = yield;
console.log(`Your response is "${response}"!`);
}
1 | const iterator = displayResponse(); |
使用数据调用 .next()
(即 .next(‘Richard’))会将该数据发送到生成器函数中上次离开的地方。它会将 yield 关键字替换为你提供的数据。
生成器是强大的新型函数,能够暂停执行代码,同时保持自己的状态。生成器适用于一次一个地循环访问列表项,以便单独处理每项,然后再转到下一项。还可以使用迭代器来处理嵌套回调。例如,假设某个函数需要获得所有仓库的列表和被加星标的次数。在获得每个仓库的星标数量之前,需要获得用户的信息。获得用户的个人资料后,代码可以利用该信息查找所有的仓库。
生成器还将大量用于 JavaScript 语言未来的新增功能中。一个即将使用生成器的新功能是异步函数。