- 变量的扩展
- let
- const
- 变量的结构
- 字符串的扩展及新增方法
- 模板字符串 (`${}`)
- matchAll 方法返回一个正则表达式在当前字符串的所有匹配
- includes 返回布尔值,表示是否找到了参数字符串。
- repeat 方法返回一个新字符串,表示将原字符串重复n次。
- trimStart 消除字符串头部的空格
- trimEnd 消除尾部的空格
- 数值的扩展
- 指数运算符 (**)
- Number.MAX_SAFE_INTEGER (2*53)
- Number.MIN_SAFE_INTEGER (-2*53)
- isSafeInteger 用来判断一个整数是否落在这个范围之内。
- Math.trunc 方法用于去除一个数的小数部分,返回整数部分。
- Math.sign 方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。
- Math.cbrt 方法用于计算一个数的立方根。
- Math.imul 方法返回两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数。
- 函数扩展
- name 属性
- 函数参数的默认值
- rest 参数
- 箭头函数
- 尾递归、尾部调用
- 数组扩展
- 扩展运算符 [...arr]
- includes
- keys、values、entries
- flat、flatMap
- 对象的扩展及新增方法
- 属性的简洁表示法
- getOwnPropertyDescriptor
- super 指向当前对象的原型对象
- Object.is
- Object.assign
- keys、values、entries、formEntries
- setPropertyOf、getPropertyOf
- Symbol
- Symbol()
- Symbol.for()
- Symbol.keyFor()
- 内置的 Symbol 值
- Set 和 Map 数据结构
- Set、WeakSet(成员只能是对象)
- Map(键值对集合)、WeakMap(键只能是对象)
- Proxy
- Reflect
- Promise
- Iterator
- Generator
- async、await
- Class
- 静态属性
- 静态方法
- 实例属性的新写法
- 私有方法
- new.target 属性
- 继承
- 构造函数
- super 关键字
- Module
- export
- import
尾调用优化
function f() {
let m = 1;
let n = 2;
return g(m + n);
}
f();
// 等同于
function f() {
return g(3);
}
f();
// 等同于
g(3);
---
// 不属于尾调用
function addOne(a){
var one = 1;
function inner(b){
return b + one;
}
return inner(a);
}
尾递归优化
function factorial(n) {
if (n === 1) return 1;
return n * factorial(n - 1);
}
factorial(5) // 120
---
// 尾递归优化
function factorial(n, total) {
if (n === 1) return total;
return factorial(n - 1, n * total);
}
factorial(5, 1) // 120
模块的 Singleton 模式
// mod.js
const FOO_KEY = Symbol.for('foo');
function A() {
this.foo = 'hello';
}
if (!global[FOO_KEY]) {
global[FOO_KEY] = new A();
}
module.exports = global[FOO_KEY];
const a = require('./mod.js');
a.foo // 'hello'
global[Symbol.for('foo')] = { foo: 'world' };
const a = require('./mod.js');
a.foo // 'world'
内置的 Symbol 值
// Symbol.hasInstance
class MyClass {
[Symbol.hasInstance](foo) {
return foo instanceof Array;
}
}
[1, 2, 3] instanceof new MyClass() // true
class Even {
static [Symbol.hasInstance](obj) {
return Number(obj) % 2 === 0;
}
}
// 等同于
const Even = {
[Symbol.hasInstance](obj) {
return Number(obj) % 2 === 0;
}
};
1 instanceof Even // false
2 instanceof Even // true
12345 instanceof Even // false
// Symbol.isConcatSpreadable
let arr1 = ['c', 'd'];
['a', 'b'].concat(arr1, 'e') // ['a', 'b', 'c', 'd', 'e']
arr1[Symbol.isConcatSpreadable] // undefined
let arr2 = ['c', 'd'];
arr2[Symbol.isConcatSpreadable] = false;
['a', 'b'].concat(arr2, 'e') // ['a', 'b', ['c','d'], 'e']
---
let obj = {length: 2, 0: 'c', 1: 'd'};
['a', 'b'].concat(obj, 'e') // ['a', 'b', obj, 'e']
obj[Symbol.isConcatSpreadable] = true;
['a', 'b'].concat(obj, 'e') // ['a', 'b', 'c', 'd', 'e']
---
class A1 extends Array {
constructor(args) {
super(args);
this[Symbol.isConcatSpreadable] = true;
}
}
class A2 extends Array {
constructor(args) {
super(args);
}
get [Symbol.isConcatSpreadable] () {
return false;
}
}
let a1 = new A1();
a1[0] = 3;
a1[1] = 4;
let a2 = new A2();
a2[0] = 5;
a2[1] = 6;
[1, 2].concat(a1).concat(a2)
// [1, 2, 3, 4, [5, 6]]
// Symbol.match
String.prototype.match(regexp)
// 等同于
regexp[Symbol.match](this)
class MyMatcher {
[Symbol.match](string) {
return 'hello world'.indexOf(string);
}
}
'e'.match(new MyMatcher()) // 1
Proxy & Reflect
// var proxy = new Proxy(target, handler);
// Reflect.get(target, name, receiver)
// Reflect.set(target, name, value, receiver)
var obj = new Proxy({}, {
get: function (target, propKey, receiver) {
console.log(`getting ${propKey}!`);
return Reflect.get(target, propKey, receiver);
},
set: function (target, propKey, value, receiver) {
console.log(`setting ${propKey}!`);
return Reflect.set(target, propKey, value, receiver);
}
});
obj.count = 1
// setting count!
++obj.count
// getting count!
// setting count!
// 2
Reflect.apply(target, thisArg, args)
Reflect.construct(target, args)
Reflect.get(target, name, receiver)
Reflect.set(target, name, value, receiver)
Reflect.defineProperty(target, name, desc)
Reflect.deleteProperty(target, name)
Reflect.has(target, name)
Reflect.ownKeys(target)
Reflect.isExtensible(target)
Reflect.preventExtensions(target)
Reflect.getOwnPropertyDescriptor(target, name)
Reflect.getPrototypeOf(target)
Reflect.setPrototypeOf(target, prototype)
Promise
- 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果
缺点:Promise也有一些缺点。首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
// 基本用法
const promise = new Promise(function(resolve, reject) {
if (true){
resolve(value);
} else {
reject(error);
}
});
// then
promise.then(function(value) {
// success
}, function(error) {
// failure
});
// catch、finally
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
// Promise.all() 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
//(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
//(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
const p = Promise.all([p1, p2, p3]);
// Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
// 只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
const p = Promise.race([p1, p2, p3]);
const p = Promise.race([
fetch('/resource-that-may-take-a-while'),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('request timeout')), 5000)
})
]);
p
.then(console.log)
.catch(console.error);
// Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。
const resolved = Promise.resolve(42);
const rejected = Promise.reject(-1);
const allSettledPromise = Promise.allSettled([resolved, rejected]);
allSettledPromise.then(function (results) {
console.log(results);
});
// Promise.any()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。
var resolved = Promise.resolve(42);
var rejected = Promise.reject(-1);
var alsoRejected = Promise.reject(Infinity);
Promise.any([resolved, rejected, alsoRejected]).then(function (result) {
console.log(result); // 42
});
Promise.any([rejected, alsoRejected]).catch(function (results) {
console.log(results); // [-1, Infinity]
});
// Promise.resolve()
// Promise.reject()
Iterator
// 遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
// Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。
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};
}
};
}
// 原生具备 Iterator 接口的数据结构如下。
// Array
// Map
// Set
// String
// TypedArray
// 函数的 arguments 对象
// NodeList 对象
//一个数据结构只要部署了Symbol.iterator属性,就被视为具有 iterator 接口,就可以用for...of循环遍历它的成员。也就是说,for...of循环内部调用的是数据结构的Symbol.iterator方法。
Generator
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }
//Generator 与 Iterator 接口的关系。任意一个对象的Symbol.iterator方法,等于该对象的遍历器生成函数,调用该函数会返回该对象的一个遍历器对象。
//由于 Generator 函数就是遍历器生成函数,因此可以把 Generator 赋值给对象的Symbol.iterator属性,从而使得该对象具有 Iterator 接口。
var myIterable = {};
myIterable[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
[...myIterable] // [1, 2, 3]
// next 方法的参数,yield表达式本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。
function* foo(x) {
var y = 2 * (yield (x + 1));
var z = yield (y / 3);
return (x + y + z);
}
var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}
var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }
// for...of 循环
function* foo() {
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
return 6;
}
for (let v of foo()) {
console.log(v);
}
// Generator.prototype.throw() Generator函数返回的遍历器对象,都有一个throw方法,可以在函数体外抛出错误,然后在 Generator 函数体内捕获。
var g = function* () {
try {
yield;
} catch (e) {
console.log('内部捕获', e);
}
};
var i = g();
i.next();
try {
i.throw('a');
i.throw('b');
} catch (e) {
console.log('外部捕获', e);
}
// 上面代码中,遍历器对象i连续抛出两个错误。第一个错误被 Generator 函数体内的catch语句捕获。i第二次抛出错误,由于 Generator 函数内部的catch语句已经执行过了,不会再捕捉到这个错误了,所以这个错误就被抛出了 Generator 函数体,被函数体外的catch语句捕获。
// Generator.prototype.return()
function* gen() {
yield 1;
yield 2;
yield 3;
}
var g = gen();
g.next() // { value: 1, done: false }
g.return('foo') // { value: "foo", done: true }
g.next()
async、await
// async 函数是什么?一句话,它就是 Generator 函数的语法糖。
var fs = {
readFile: function(fileName, cb) {
setTimeout(() => {
cb && cb(false, fileName + " content");
}, 1000);
}
};
var readFile = function(fileName) {
return new Promise(function(resolve, reject) {
fs.readFile(fileName, function(error, data) {
if (error) return reject(error);
resolve(data);
});
});
};
var gen = function* () {
var f1 = yield readFile("/etc/fstab");
var f2 = yield readFile("/etc/shells");
};
const asyncReadFile = async function() {
const f1 = await readFile("/etc/fstab");
console.log(f1);
const f2 = await readFile("/etc/shells");
console.log(f2);
};
var g = gen();
g.next().value.then((res) => {
console.log(res);
});
g.next().value.then((res) => {
console.log(res);
});
asyncReadFile().then((res) => {
console.log(res);
});
//async函数对 Generator 函数的改进,体现在以下四点。
//(1)内置执行器。
//(2)更好的语义。
//(3)更广的适用性。
//(4)返回值是 Promise。
//async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。
//返回 Promise 对象,async函数内部return语句返回的值,会成为then方法回调函数的参数。
async function f() {
return 'hello world';
}
f().then(v => console.log(v))
// "hello world"
//async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到。
async function f() {
throw new Error('出错了');
}
f().then(
v => console.log(v),
e => console.log(e)
)
// Error: 出错了
//async函数返回的 Promise 对象,必须等到内部所有await命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误。也就是说,只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。
Calss
// Mixin 模式的实现
//Mixin 指的是多个对象合成一个新的对象,新对象具有各个组成成员的接口。它的最简单实现如下。
const a = {
a: 'a'
};
const b = {
b: 'b'
};
const c = {...a, ...b}; // {a: 'a', b: 'b'}
上面代码中,c对象是a对象和b对象的合成,具有两者的接口。
下面是一个更完备的实现,将多个类的接口“混入”(mix in)另一个类。
function mix(...mixins) {
class Mix {
constructor() {
for (let mixin of mixins) {
copyProperties(this, new mixin()); // 拷贝实例属性
}
}
}
for (let mixin of mixins) {
copyProperties(Mix, mixin); // 拷贝静态属性
copyProperties(Mix.prototype, mixin.prototype); // 拷贝原型属性
}
return Mix;
}
function copyProperties(target, source) {
for (let key of Reflect.ownKeys(source)) {
if ( key !== 'constructor'
&& key !== 'prototype'
&& key !== 'name'
) {
let desc = Object.getOwnPropertyDescriptor(source, key);
Object.defineProperty(target, key, desc);
}
}
}
//上面代码的mix函数,可以将多个对象合成为一个类。使用的时候,只要继承这个类即可。
class DistributedEdit extends mix(Loggable, Serializable) {
// ...
}
网友评论