ECMAScript 6 新特性详解(下)

简介: ECMAScript 6 新特性详解(下)

15、Symbols

symbol 是一种基本数据类型(primitive data type)。Symbol() 函数会返回 symbol 类型的值,该类型具有静态属性和静态方法。它的静态属性会暴露几个内建的成员对象;它的静态方法会暴露全局的 symbol 注册,且类似于内建对象类,但作为构造函数来说它并不完整,因为它不支持语法:"new Symbol()"。

每个从 Symbol() 返回的 symbol 值都是唯一的。一个 symbol 值能作为对象属性的标识符;这是该数据类型仅有的目的。

1. const symbol1 = Symbol();
2. const symbol2 = Symbol(42);
3. const symbol3 = Symbol('foo');
4. 
5. console.log(typeof symbol1);
6. // 输出: "symbol"
7. 
8. console.log(symbol2 === 42);
9. // 输出: false
10. 
11. console.log(symbol3.toString());
12. // 输出: "Symbol(foo)"
13. 
14. console.log(Symbol('foo') === Symbol('foo'));
15. // 输出: false
16. 
17. Symbol("foo") === Symbol("foo"); // false

16、继承

对于使用过基于类的语言 (如 Java 或 C++) 的开发者们来说,JavaScript 实在是有些令人困惑 —— JavaScript 是动态的,本身不提供一个 class 的实现。即便是在 ES2015/ES6 中引入了 class 关键字,但那也只是语法糖,JavaScript 仍然是基于原型的。

当谈到继承时,JavaScript 只有一种结构:对象。每个实例对象(object)都有一个私有属性(称之为 __proto__)指向它的构造函数的原型对象(prototype)。该原型对象也有一个自己的原型对象(__proto__),层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。

1. let f = function () {
2. this.a = 1;
3. this.b = 2;
4. }
5. let o = new f(); // {a: 1, b: 2}
6. 
7. // 在 f 函数的原型上定义属性
8. f.prototype.b = 3;
9. f.prototype.c = 4;
10. 
11. console.log(o.a); // 1
12. console.log(o.b); // 2    优先找实例上的属性
13. console.log(o.c); // 4    优先找实例上的属性,然后从原型对象上找

使用语法结构创建的对象:

1. var o = {a: 1};
2. // o 这个对象继承了 Object.prototype 上面的所有属性
3. // o 自身没有名为 hasOwnProperty 的属性
4. // hasOwnProperty 是 Object.prototype 的属性
5. // 因此 o 继承了 Object.prototype 的 hasOwnProperty
6. // Object.prototype 的原型为 null
7. // 原型链如下:
8. // o ---> Object.prototype ---> null
9. var a = ["yo", "whadup", "?"];
10. // 数组都继承于 Array.prototype
11. // (Array.prototype 中包含 indexOf, forEach 等方法)
12. // 原型链如下:
13. // a ---> Array.prototype ---> Object.prototype ---> null
14. function f(){
15. return 2;
16. }
17. // 函数都继承于 Function.prototype
18. // (Function.prototype 中包含 call, bind 等方法)
19. // 原型链如下:
20. // f ---> Function.prototype ---> Object.prototype ---> null

使用构造器创建的对象:

在 JavaScript 中,构造器其实就是一个普通的函数。当使用 new 操作符 来作用这个函数时,它就可以被称为构造方法(构造函数)

1. function Graph() {
2. this.vertices = [];
3. this.edges = [];
4. }
5. Graph.prototype = {
6. addVertex: function(v){
7.  this.vertices.push(v);
8.   }
9. };
10. var g = new Graph();
11. // g 是生成的对象,他的自身属性有 'vertices' 和 'edges'。
12. // 在 g 被实例化时,g.[[Prototype]] 指向了 Graph.prototype。

使用Object.create创建的对象:

1. var a = {a: 1};
2. // a ---> Object.prototype ---> null
3. var b = Object.create(a);
4. // b ---> a ---> Object.prototype ---> null
5. console.log(b.a); // 1 (继承而来)
6. var c = Object.create(b);
7. // c ---> b ---> a ---> Object.prototype ---> null
8. var d = Object.create(null);
9. // d ---> null
10. console.log(d.hasOwnProperty); // undefined,因为 d 没有继承 Object.prototype

使用class关键字创建的对象:

ECMAScript6 引入了一套新的关键字用来实现 class。使用基于类语言的开发人员会对这些结构感到熟悉,但它们是不同的。JavaScript 仍然基于原型。这些新的关键字包括 class, constructor,static,extends 和 super。

1. "use strict";
2. class Polygon {
3. constructor(height, width) {
4. this.height = height;
5. this.width = width;
6.   }
7. }
8. class Square extends Polygon {
9. constructor(sideLength) {
10.   super(sideLength, sideLength);
11.   }
12. get area() {
13.   return this.height * this.width;
14.   }
15. set sideLength(newLength) {
16. this.height = newLength;
17. this.width = newLength;
18.   }
19. }
20. var square = new Square(2);

性能:

在原型链上查找属性比较耗时,对性能有副作用,这在性能要求苛刻的情况下很重要。另外,试图访问不存在的属性时会遍历整个原型链。

遍历对象的属性时,原型链上的每个可枚举属性都会被枚举出来。要检查对象是否具有自己定义的属性,而不是其原型链上的某个属性,则必须使用所有对象从 Object.prototype 继承的 hasOwnProperty (en-US) 方法。下面给出一个具体的例子来说明它:

1. console.log(g.hasOwnProperty('vertices'));
2. // true
3. console.log(g.hasOwnProperty('nope'));
4. // false
5. console.log(g.hasOwnProperty('addVertex'));
6. // false
7. console.log(g.__proto__.hasOwnProperty('addVertex'));
8. // true

hasOwnProperty (en-US) 是 JavaScript 中唯一一个处理属性并且不会遍历原型链的方法。(译者注:原文如此。另一种这样的方法:Object.keys())

注意:检查属性是否为 undefined 是不能够检查其是否存在的。该属性可能已存在,但其值恰好被设置成了 undefined。

17、Promise

Promise 对象用于表示一个异步操作的最终完成(或失败)及其结果值。

此特性在 Web Worker 中可用

一个 Promise 对象代表一个在这个 promise 被创建出来时不一定已知值的代理。它让你能够把异步操作最终的成功返回值或者失败原因和相应的处理程序关联起来。这样使得异步方法可以像同步方法那样返回值:异步方法并不会立即返回最终的值,而是会返回一个 promise,以便在未来某个时候把值交给使用者。

一个 Promise 必然处于以下几种状态之一:

  • 待定(pending):初始状态,既没有被兑现,也没有被拒绝。
  • 已兑现(fulfilled):意味着操作成功完成。
  • 已拒绝(rejected):意味着操作失败。

待定状态的 Promise 对象要么会通过一个值被兑现,要么会通过一个原因(错误)被拒绝。当这些情况之一发生时,我们用 promise 的 then 方法排列起来的相关处理程序就会被调用。如果 promise 在一个相应的处理程序被绑定时就已经被兑现或被拒绝了,那么这个处理程序也同样会被调用,因此在完成异步操作和绑定处理方法之间不存在竞态条件。

因为 Promise.prototype.then 和 Promise.prototype.catch 方法返回的是 promise,所以它们可以被链式调用。

如果一个 promise 已经被兑现或被拒绝,那么我们也可以说它处于 已敲定(settled) 状态。你还会听到一个经常跟 promise 一起使用的术语:已决议(resolved),它表示 promise 已经处于已敲定状态,或者为了匹配另一个 promise 的状态被“锁定”了。

Promise 的链式调用

我们可以用 Promise.prototype.then()、Promise.prototype.catch() 和 Promise.prototype.finally() 这些方法将进一步的操作与一个变为已敲定状态的 promise 关联起来。

例如 .then() 方法需要两个参数,第一个参数作为处理已兑现状态的回调函数,而第二个参数则作为处理已拒绝状态的回调函数。每一个 .then() 方法还会返回一个新生成的 promise 对象,这个对象可被用作链式调用,就像这样:

1. const myPromise = new Promise((resolve, reject) => {
2. setTimeout(() => {
3. resolve('foo');
4.   }, 300);
5. });
6. 
7. myPromise
8.   .then(value => { return value + ' and bar'; })
9.   .then(value => { return value + ' and bar again'; })
10.   .then(value => { return value + ' and again'; })
11.   .then(value => { return value + ' and again'; })
12.   .then(value => { console.log(value) })
13.   .catch(err => { console.log(err) });
14. // foo and bar and bar again and again and again

链式调用中的 promise 们就像俄罗斯套娃一样,是嵌套起来的,但又像是一个栈,每个都必须从顶端被弹出。链式调用中的第一个 promise 是嵌套最深的一个,也将是第一个被弹出的。

(promise D, (promise C, (promise B, (promise A) ) ) )

当存在一个 nextValue 是 promise 时,就会出现一种动态的替换效果。return 会导致一个 promise 被弹出,但这个 nextValue promise 则会被推入被弹出 promise 原来的位置。对于上面所示的嵌套场景,假设与 "promise B" 相关的 .then() 返回了一个值为 "promise X" 的 nextValue 。那么嵌套的结果看起来就会是这样:

(promise D, (promise C, (promise X) ) )

静态方法:

Promise.all(iterable)

这个方法返回一个新的 promise 对象,等到所有的 promise 对象都成功或有任意一个 promise 失败。

如果所有的 promise 都成功了,它会把一个包含 iterable 里所有 promise 返回值的数组作为成功回调的返回值。顺序跟 iterable 的顺序保持一致。

一旦有任意一个 iterable 里面的 promise 对象失败则立即以该 promise 对象失败的理由来拒绝这个新的 promise。

Promise.allSettled(iterable)

等到所有 promise 都已敲定(每个 promise 都已兑现或已拒绝)。

返回一个 promise,该 promise 在所有 promise 都敲定后完成,并兑现一个对象数组,其中的对象对应每个 promise 的结果。

Promise.any(iterable)

接收一个 promise 对象的集合,当其中的任意一个 promise 成功,就返回那个成功的 promise 的值。

Promise.race(iterable)

等到任意一个 promise 的状态变为已敲定。

当 iterable 参数里的任意一个子 promise 成功或失败后,父 promise 马上也会用子 promise 的成功返回值或失败详情作为参数调用父 promise 绑定的相应处理函数,并返回该 promise 对象。

Promise.reject(reason)

返回一个状态为已拒绝的 Promise 对象,并将给定的失败信息传递给对应的处理函数。

Promise.resolve(value)

返回一个状态由给定 value 决定的 Promise 对象。如果该值是 thenable(即,带有 then 方法的对象),返回的 Promise 对象的最终状态由 then 方法执行结果决定;否则,返回的 Promise 对象状态为已兑现,并且将该 value 传递给对应的 then 方法。

18、Math + Number + String + Array + Object APIs

1. Number.EPSILON
2. Number.isInteger(Infinity) // false
3. Number.isNaN("NaN") // false
4. 
5. Math.acosh(3) // 1.762747174039086
6. Math.hypot(3, 4) // 5
7. Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2
8. 
9. "abcde".includes("cd") // true
10. "abc".repeat(3) // "abcabcabc"
11. 
12. Array.from(document.querySelectorAll('*')) // Returns a real Array
13. Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior
14. [0, 0, 0].fill(7, 1) // [0,7,7]
15. [1, 2, 3].find(x => x == 3) // 3
16. [1, 2, 3].findIndex(x => x == 2) // 1
17. [1, 2, 3, 4, 5].copyWithin(3, 0) // [1, 2, 3, 1, 2]
18. ["a", "b", "c"].entries() // iterator [0, "a"], [1,"b"], [2,"c"]
19. ["a", "b", "c"].keys() // iterator 0, 1, 2
20. ["a", "b", "c"].values() // iterator "a", "b", "c"
21. 
22. Object.assign(Point, { origin: new Point(0,0) })

19、二进制,八进制

为二进制(b)和八进制(o)添加了两种新的数字文字形式。

1. 0b111110111 === 503 // true
2. 0o767 === 503 // true

20、Reflect API

Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与 proxy handler (en-US) 的方法相同。Reflect 不是一个函数对象,因此它是不可构造的。

与大多数全局对象不同 Reflect 并非一个构造函数,所以不能通过 new 运算符对其进行调用,或者将 Reflect 对象作为一个函数来调用。Reflect 的所有属性和方法都是静态的(就像 Math 对象)。

Reflect.apply(target, thisArgument, argumentsList)

对一个函数进行调用操作,同时可以传入一个数组作为调用参数。和 Function.prototype.apply() 功能类似。

Reflect.construct(target, argumentsList[, newTarget])

对构造函数进行 new 操作,相当于执行 new target(...args)。

Reflect.defineProperty(target, propertyKey, attributes)

和 Object.defineProperty() 类似。如果设置成功就会返回 true

Reflect.deleteProperty(target, propertyKey)

作为函数的delete操作符,相当于执行 delete target[name]。

Reflect.get(target, propertyKey[, receiver])

获取对象身上某个属性的值,类似于 target[name]。

Reflect.getOwnPropertyDescriptor(target, propertyKey)

类似于 Object.getOwnPropertyDescriptor()。如果对象中存在该属性,则返回对应的属性描述符,否则返回 undefined。

Reflect.getPrototypeOf(target)

类似于 Object.getPrototypeOf()。

Reflect.has(target, propertyKey)

判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同。

Reflect.isExtensible(target)

类似于 Object.isExtensible().

Reflect.ownKeys(target)

返回一个包含所有自身属性(不包含继承属性)的数组。(类似于 Object.keys(), 但不会受enumerable 影响).

Reflect.preventExtensions(target)

类似于 Object.preventExtensions()。返回一个Boolean。

Reflect.set(target, propertyKey, value[, receiver])

将值分配给属性的函数。返回一个Boolean,如果更新成功,则返回true。

Reflect.setPrototypeOf(target, prototype)

设置对象原型的函数。返回一个 Boolean,如果更新成功,则返回 true。

1. const duck = {
2. 
3. name: 'Maurice',
4. 
5. color: 'white',
6. 
7. greeting: function() {
8. 
9. console.log(`Quaaaack! My name is ${this.name}`);
10. 
11. }
12. 
13. }
14. 
15. Reflect.has(duck, 'color');
16. 
17. // true
18. 
19. Reflect.has(duck, 'haircut');
20. 
21. // false
22. Reflect.ownKeys(duck);
23. // [ "name", "color", "greeting" ]
24. Reflect.ownKeys(duck);
25. // [ "name", "color", "greeting" ]

21、尾递归

尾部位置的调用保证不会无限增长堆栈。使递归算法在面对没有超出边界的输入时是安全的。

之前直接用递归实现:

1. function factorial(n){
2. if(n === 1) return 1; 
3. return n * factorial( n -1 ); 
4. }

使用return 返回的是一个表达式,会在堆栈中进行存储,当数超过堆栈限定的值时,会导致堆栈溢出。

使用尾递归实现:

1. function factorial(n, acc = 1) {
2. 'use strict';
3. if (n <= 1) return acc;
4. return factorial(n - 1, n * acc);
5. }
6. // 当今大多数实现中会导致堆栈溢出,
7. // 但在ES6中的任意输入上是安全的
8. factorial(100000)


相关文章
|
7月前
|
前端开发 JavaScript 索引
ECMAScript 2024 新特性
ECMAScript 2024 新特性 ECMAScript 2024,第 15 版,添加了用于调整 ArrayBuffer 和 SharedArrayBuffer 大小和传输的功能; 添加了一个新的 RegExp /v 标志,用于创建具有更高级功能的 RegExp,用于处理字符串集; 并介绍了用于构造 Promise 的 Promise.withResolvers 便捷方法、用于聚合数据的 Object.groupBy 和 Map.groupBy 方法等
111 1
|
8月前
|
存储 JavaScript 前端开发
ECMAScript 2022 正式发布,有哪些新特性?(上)
ECMAScript 2022 正式发布,有哪些新特性?(上)
169 0
|
8月前
|
自然语言处理 JavaScript 前端开发
ECMAScript 2022 正式发布,有哪些新特性?(下)
ECMAScript 2022 正式发布,有哪些新特性?(下)
|
8月前
|
JavaScript 前端开发 Unix
ECMAScript 2023 正式发布,有哪些新特性?
ECMAScript 2023 正式发布,有哪些新特性?
153 0
|
自然语言处理 JavaScript 前端开发
ECMAScript 6 新特性详解(上)
ECMAScript 6 新特性详解(上)
|
存储 JSON JavaScript
ECMAScript 6 新特性详解(中)
ECMAScript 6 新特性详解(中)
|
JSON JavaScript 前端开发
ECMAScript6.0基础
1.什么是ES6 ECMAScript 6.0(以下简称ES6)是JavaScript语言的下一代标准,已经在2015年6月正式发布了。它的目标,是使得JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言。 标准的制定者有计划,以后每年发布一次标准,使用年份作为版本。因为ES6的第一个版本是在2015年发布的,所以又称ECMAScript 2015(简称ES2015)。
286 0
ECMAScript6.0基础
|
JavaScript 前端开发
ECMAScript 6新特性简介
ECMAScript 6新特性简介
ECMAScript 6新特性简介
|
Web App开发 前端开发 JavaScript
ECMAScript 2020(ES11) 的新特性总结
ECMAScript 2020(ES11) 的新特性总结
220 0
ECMAScript 2020(ES11) 的新特性总结
|
JavaScript Linux
ECMAScript 2017(ES8) 的新特性总结
ECMAScript 2017(ES8) 的新特性总结
227 0
ECMAScript 2017(ES8) 的新特性总结

热门文章

最新文章

下一篇
开通oss服务