1
0
mirror of https://github.com/ruanyf/es6tutorial.git synced 2025-05-24 18:32:22 +00:00

doc: add ES7 stage 3 features

This commit is contained in:
ruanyf 2016-02-09 09:23:29 +08:00
parent 0be4b18312
commit 40437de61a
6 changed files with 420 additions and 15 deletions

View File

@ -691,7 +691,7 @@ function* somethingAsync(x) {
} }
``` ```
上面的代码允许并发三个somethingAsync异步操作等到它们全部完成才会进行下一步。 上面的代码允许并发三个`somethingAsync`异步操作,等到它们全部完成,才会进行下一步。
## async函数 ## async函数
@ -721,7 +721,7 @@ var gen = function* (){
}; };
``` ```
写成 async 函数,就是下面这样。 写成`async`函数,就是下面这样。
```javascript ```javascript
var asyncReadFile = async function (){ var asyncReadFile = async function (){
@ -734,7 +734,7 @@ var asyncReadFile = async function (){
一比较就会发现,`async`函数就是将Generator函数的星号`*`)替换成`async`,将`yield`替换成`await`,仅此而已。 一比较就会发现,`async`函数就是将Generator函数的星号`*`)替换成`async`,将`yield`替换成`await`,仅此而已。
`async`函数对 Generator 函数的改进,体现在以下点。 `async`函数对 Generator 函数的改进,体现在以下点。
1内置执行器。Generator函数的执行必须靠执行器所以才有了`co`模块,而`async`函数自带执行器。也就是说,`async`函数的执行,与普通函数一模一样,只要一行。 1内置执行器。Generator函数的执行必须靠执行器所以才有了`co`模块,而`async`函数自带执行器。也就是说,`async`函数的执行,与普通函数一模一样,只要一行。
@ -752,6 +752,8 @@ var result = asyncReadFile();
进一步说async函数完全可以看作多个异步操作包装成的一个Promise对象`await`命令就是内部`then`命令的语法糖。 进一步说async函数完全可以看作多个异步操作包装成的一个Promise对象`await`命令就是内部`then`命令的语法糖。
正常情况下,`await`命令后面是一个Promise对象否则会被转成Promise。
### async函数的实现 ### async函数的实现
async 函数的实现,就是将 Generator 函数和自动执行器,包装在一个函数里。 async 函数的实现,就是将 Generator 函数和自动执行器,包装在一个函数里。
@ -770,9 +772,9 @@ function fn(args){
} }
``` ```
所有的 async 函数都可以写成上面的第二种形式,其中的 spawn 函数就是自动执行器。 所有的`async`函数都可以写成上面的第二种形式,其中的 spawn 函数就是自动执行器。
下面给出 spawn 函数的实现,基本就是前文自动执行器的翻版。 下面给出`spawn`函数的实现,基本就是前文自动执行器的翻版。
```javascript ```javascript
function spawn(genF) { function spawn(genF) {
@ -798,11 +800,11 @@ function spawn(genF) {
} }
``` ```
async 函数是非常新的语法功能,新到都不属于 ES6而是属于 ES7。目前它仍处于提案阶段但是转码器 Babel 和 regenerator 都已经支持,转码后就能使用。 `async`函数是非常新的语法功能,新到都不属于 ES6而是属于 ES7。目前它仍处于提案阶段但是转码器`Babel``regenerator`都已经支持,转码后就能使用。
### async 函数的用法 ### async 函数的用法
同Generator函数一样async函数返回一个Promise对象可以使用then方法添加回调函数。当函数执行的时候一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。 同Generator函数一样`async`函数返回一个Promise对象可以使用`then`方法添加回调函数。当函数执行的时候,一旦遇到`await`就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。
下面是一个例子。 下面是一个例子。
@ -813,12 +815,12 @@ async function getStockPriceByName(name) {
return stockPrice; return stockPrice;
} }
getStockPriceByName('goog').then(function (result){ getStockPriceByName('goog').then(function (result) {
console.log(result); console.log(result);
}); });
``` ```
上面代码是一个获取股票报价的函数函数前面的async关键字表明该函数内部有异步操作。调用该函数时会立即返回一个Promise对象。 上面代码是一个获取股票报价的函数,函数前面的`async`关键字,表明该函数内部有异步操作。调用该函数时,会立即返回一个`Promise`对象。
下面的例子,指定多少毫秒后输出一个值。 下面的例子,指定多少毫秒后输出一个值。
@ -839,6 +841,22 @@ asyncPrint('hello world', 50);
上面代码指定50毫秒以后输出"hello world"。 上面代码指定50毫秒以后输出"hello world"。
Async函数有多种使用形式。
```javascript
// 函数声明
async function foo() {}
// 函数表达式
const foo = async function () {};
// 对象的方法
let obj = { async foo() {} }
// 箭头函数
const foo = async () => {};
```
### 注意点 ### 注意点
第一点,`await`命令后面的Promise对象运行结果可能是rejected所以最好把`await`命令放在`try...catch`代码块中。 第一点,`await`命令后面的Promise对象运行结果可能是rejected所以最好把`await`命令放在`try...catch`代码块中。

View File

@ -729,6 +729,13 @@ Object.values(obj) // []
上面代码中,`Object.create`方法的第二个参数添加的对象属性(属性`p`),如果不显式声明,默认是不可遍历的。`Object.values`不会返回这个属性。 上面代码中,`Object.create`方法的第二个参数添加的对象属性(属性`p`),如果不显式声明,默认是不可遍历的。`Object.values`不会返回这个属性。
`Object.values`会过滤属性名为Symbol值的属性。
```javascript
Object.entries({ [Symbol()]: 123, foo: 'abc' });
// ['abc']
```
如果`Object.values`方法的参数是一个字符串,会返回各个字符组成的一个数组。 如果`Object.values`方法的参数是一个字符串,会返回各个字符组成的一个数组。
```javascript ```javascript
@ -757,6 +764,26 @@ Object.entries(obj)
除了返回值不一样,该方法的行为与`Object.values`基本一致。 除了返回值不一样,该方法的行为与`Object.values`基本一致。
如果原对象的属性名是一个Symbol值该属性会被省略。
```javascript
Object.entries({ [Symbol()]: 123, foo: 'abc' });
// [ [ 'foo', 'abc' ] ]
```
上面代码中,原对象有两个属性,`Object.entries`只输出属性名非Symbol值的属性。将来可能会有`Reflect.ownEntries()`方法,返回对象自身的所有属性。
`Object.entries`的基本用途是遍历对象的属性。
```javascript
let obj = { one: 1, two: 2 };
for (let [k, v] of Object.entries(obj)) {
console.log(`${JSON.stringify(k)}: ${JSON.stringify(v)}`);
}
// "one": 1
// "two": 2
```
`Object.entries`方法的一个用处是,将对象转为真正的`Map`结构。 `Object.entries`方法的一个用处是,将对象转为真正的`Map`结构。
```javascript ```javascript
@ -896,3 +923,174 @@ let runtimeError = {
```javascript ```javascript
let emptyObject = { ...null, ...undefined }; // 不报错 let emptyObject = { ...null, ...undefined }; // 不报错
``` ```
## Object.getOwnPropertyDescriptors()
ES5有一个`Object.getOwnPropertyDescriptor`方法返回某个对象属性的描述对象descriptor
```javascript
var obj = { p: 'a' };
Object.getOwnPropertyDescriptor(obj, 'p')
// Object { value: "a",
// writable: true,
// enumerable: true,
// configurable: true
// }
```
ES7有一个提案提出了`Object.getOwnPropertyDescriptors`方法,返回指定对象所有自身属性(非继承属性)的描述对象。
```javascript
const obj = {
foo: 123,
get bar() { return 'abc' },
};
Object.getOwnPropertyDescriptors(obj)
// { foo:
// { value: 123,
// writable: true,
// enumerable: true,
// configurable: true },
// bar:
// { get: [Function: bar],
// set: undefined,
// enumerable: true,
// configurable: true } }
```
`Object.getOwnPropertyDescriptors`方法返回一个对象,所有原对象的属性名都是该对象的属性名,对应的属性值就是该属性的描述对象。
该方法的实现非常容易。
```javascript
function getOwnPropertyDescriptors(obj) {
const result = {};
for (let key of Reflect.ownKeys(obj)) {
result[key] = Object.getOwnPropertyDescriptor(obj, key);
}
return result;
}
```
该方法的提出目的,主要是为了解决`Object.assign()`无法正确拷贝`get`属性和`set`属性的问题。
```javascript
const source = {
set foo(value) {
console.log(value);
}
};
const target1 = {};
Object.assign(target1, source);
Object.getOwnPropertyDescriptor(target1, 'foo')
// { value: undefined,
// writable: true,
// enumerable: true,
// configurable: true }
```
上面代码中,`source`对象的`foo`属性的值是一个赋值函数,`Object.assign`方法将这个属性拷贝给`target1`对象,结果该属性的值变成了`undefined`。这是因为`Object.assign`方法总是拷贝一个属性的值,而不会拷贝它背后的赋值方法或取值方法。
这时,`Object.getOwnPropertyDescriptors`方法配合`Object.defineProperties`方法,就可以实现正确拷贝。
```javascript
const source = {
set foo(value) {
console.log(value);
}
};
const target2 = {};
Object.defineProperties(target2, Object.getOwnPropertyDescriptors(source));
Object.getOwnPropertyDescriptor(target2, 'foo')
// { get: undefined,
// set: [Function: foo],
// enumerable: true,
// configurable: true }
```
上面代码中,将两个对象合并的逻辑提炼出来,就是下面这样。
```javascript
const shallowMerge = (target, source) => Object.defineProperties(
target,
Object.getOwnPropertyDescriptors(source)
);
```
`Object.getOwnPropertyDescriptors`方法的另一个用处,是配合`Object.create`方法,将对象属性克隆到一个新对象。这属于浅拷贝。
```javascript
const clone = Object.create(Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj));
// 或者
const shallowClone = (obj) => Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
);
```
上面代码会克隆对象`obj`
另外,`Object.getOwnPropertyDescriptors`方法可以实现,一个对象继承另一个对象。以前,继承另一个对象,常常写成下面这样。
```javascript
const obj = {
__proto__: prot,
foo: 123,
};
```
ES6规定`__proto__`只有浏览器要部署,其他环境不用部署。如果去除`__proto__`,上面代码就要改成下面这样。
```javascript
const obj = Object.create(prot);
obj.foo = 123;
// 或者
const obj = Object.assign(
Object.create(prot),
{
foo: 123,
}
);
```
有了`Object.getOwnPropertyDescriptors`,我们就有了另一种写法。
```javascript
const obj = Object.create(
prot,
Object.getOwnPropertyDescriptors({
foo: 123,
})
);
```
`Object.getOwnPropertyDescriptors`也可以用来实现Mixin混入模式。
```javascript
let mix = (object) => ({
with: (...mixins) => mixins.reduce(
(c, mixin) => Object.create(
c, Object.getOwnPropertyDescriptors(mixin)
), object)
});
// multiple mixins example
let a = {a: 'a'};
let b = {b: 'b'};
let c = {c: 'c'};
let d = mix(c).with(a, b);
```
上面代码中,对象`a``b`被混入了对象`c`
出于完整性的考虑,`Object.getOwnPropertyDescriptors`进入标准以后,还会有`Reflect.getOwnPropertyDescriptors`方法。

View File

@ -245,7 +245,7 @@ arr[-1] // c
上面代码中,数组的位置参数是`-1`,就会输出数组的倒数最后一个成员。 上面代码中,数组的位置参数是`-1`,就会输出数组的倒数最后一个成员。
利用proxy可以将读取属性的操作`get`),转变为执行某个函数,从而实现属性的链式操作。 利用Proxy可以将读取属性的操作`get`),转变为执行某个函数,从而实现属性的链式操作。
```javascript ```javascript
var pipe = (function () { var pipe = (function () {
@ -276,11 +276,46 @@ pipe(3).double.pow.reverseInt.get
上面代码设置Proxy以后达到了将函数名链式使用的效果。 上面代码设置Proxy以后达到了将函数名链式使用的效果。
下面的例子则是利用`get`拦截实现一个生成各种DOM节点的通用函数`dom`
```javascript
const el = dom.div({},
'Hello, my name is ',
dom.a({href: '//example.com'}, 'Mark'),
'. I like:',
dom.ul({},
dom.li({}, 'The web'),
dom.li({}, 'Food'),
dom.li({}, '…actually that\'s it')
)
);
document.body.appendChild(el);
const dom = new Proxy({}, {
get(target, property) {
return function(attrs = {}, ...children) {
const el = document.createElement(property);
for (let prop of Object.keys(attrs)) {
el.setAttribute(prop, attrs[prop]);
}
for (let child of children) {
if (typeof child === 'string') {
child = document.createTextNode(child);
}
el.appendChild(child);
}
return el;
}
}
});
```
### set() ### set()
`set`方法用来拦截某个属性的赋值操作。 `set`方法用来拦截某个属性的赋值操作。
假定Person对象有一个`age`属性该属性应该是一个不大于200的整数那么可以使用Proxy对象保证`age`的属性值符合要求。 假定`Person`对象有一个`age`属性该属性应该是一个不大于200的整数那么可以使用`Proxy`保证`age`的属性值符合要求。
```javascript ```javascript
let validator = { let validator = {
@ -364,8 +399,8 @@ var handler = {
var p = new Proxy(target, handler); var p = new Proxy(target, handler);
p() === 'I am the proxy'; p()
// true // "I am the proxy"
``` ```
上面代码中,变量`p`是Proxy的实例当它作为函数调用时`p()`),就会被`apply`方法拦截,返回一个字符串。 上面代码中,变量`p`是Proxy的实例当它作为函数调用时`p()`),就会被`apply`方法拦截,返回一个字符串。
@ -387,7 +422,7 @@ proxy.call(null, 5, 6) // 22
proxy.apply(null, [7, 8]) // 30 proxy.apply(null, [7, 8]) // 30
``` ```
上面代码中,每当执行`proxy`函数,就会被`apply`方法拦截。 上面代码中,每当执行`proxy`函数(直接调用或`call``apply`调用),就会被`apply`方法拦截。
另外,直接调用`Reflect.apply`方法,也会被拦截。 另外,直接调用`Reflect.apply`方法,也会被拦截。

View File

@ -82,6 +82,8 @@
- Sella Rafaeli, [Native JavaScript Data-Binding](http://www.sellarafaeli.com/blog/native_javascript_data_binding): 如何使用Object.observe方法实现数据对象与DOM对象的双向绑定 - Sella Rafaeli, [Native JavaScript Data-Binding](http://www.sellarafaeli.com/blog/native_javascript_data_binding): 如何使用Object.observe方法实现数据对象与DOM对象的双向绑定
- Axel Rauschmayer, [`__proto__` in ECMAScript 6](http://www.2ality.com/2015/09/proto-es6.html) - Axel Rauschmayer, [`__proto__` in ECMAScript 6](http://www.2ality.com/2015/09/proto-es6.html)
- Axel Rauschmayer, [Enumerability in ECMAScript 6](http://www.2ality.com/2015/10/enumerability-es6.html) - Axel Rauschmayer, [Enumerability in ECMAScript 6](http://www.2ality.com/2015/10/enumerability-es6.html)
- Axel Rauschmayer, [ES proposal: Object.getOwnPropertyDescriptors()](http://www.2ality.com/2016/02/object-getownpropertydescriptors.html)
- TC39, [Object.getOwnPropertyDescriptors Proposal](https://github.com/tc39/proposal-object-getownpropertydescriptors)
## Proxy和Reflect ## Proxy和Reflect

View File

@ -1,5 +1,7 @@
# SIMD 的用法 # SIMD 的用法
## 概述
SIMD是“Single Instruction/Multiple Data”的缩写意为“单指令多数据”。它是JavaScript操作CPU对应指令的接口你可以看做这是一种不同的运算执行模式。与它相对的是SISD“Single Instruction/Single Data”即“单指令单数据”。 SIMD是“Single Instruction/Multiple Data”的缩写意为“单指令多数据”。它是JavaScript操作CPU对应指令的接口你可以看做这是一种不同的运算执行模式。与它相对的是SISD“Single Instruction/Single Data”即“单指令单数据”。
SIMD的含义是使用一个指令完成多个数据的运算SISD的含义是使用一个指令完成单个数据的运算这是JavaScript的默认运算模式。显而易见SIMD的执行效率要高于SISD所以被广泛用于3D图形运算、物理模拟等运算量超大的项目之中。 SIMD的含义是使用一个指令完成多个数据的运算SISD的含义是使用一个指令完成单个数据的运算这是JavaScript的默认运算模式。显而易见SIMD的执行效率要高于SISD所以被广泛用于3D图形运算、物理模拟等运算量超大的项目之中。
@ -25,9 +27,152 @@ c; // Array[6, 8, 10, 12]
```javascript ```javascript
var a = SIMD.Float32x4(1, 2, 3, 4); var a = SIMD.Float32x4(1, 2, 3, 4);
var b = SIMD.Float32x4(5, 6, 7, 8); var b = SIMD.Float32x4(5, 6, 7, 8);
var c = SIMD.Float32x4.add(a,b); // Float32x4[6, 8, 10, 12] var c = SIMD.Float32x4.add(a, b); // Float32x4[6, 8, 10, 12]
``` ```
上面代码之中,数组`a``b`的四个成员的各自相加只用一条指令就完成了。因此速度比上一种写法提高了4倍。 上面代码之中,数组`a``b`的四个成员的各自相加只用一条指令就完成了。因此速度比上一种写法提高了4倍。
一次SIMD运算可以处理多个数据这些数据被称为“通道”lane。上面代码中一次运算了四个数据因此就是四个通道。 一次SIMD运算可以处理多个数据这些数据被称为“通道”lane。上面代码中一次运算了四个数据因此就是四个通道。
SIMD通常用于矢量运算。
```javascript
v + w = 〈v1, …, vn〉+ 〈w1, …, wn〉
= 〈v1+w1, …, vn+wn〉
```
上面代码中,`v``w`是两个多元矢量。它们的加运算在SIMD下是一个指令、而不是n个指令完成的这就大大提高了效率。这对于3D动画、图像处理、信号处理、数值处理、加密等运算是非常重要的。
总得来说SIMD是数据并行处理parallelism的一种手段。
## 数据类型
SIMD提供多种数据类型。
- Float32x4四个32位浮点数
- Float64x2两个64位浮点数
- Int32x4四个32位整数
- Int16x8八个16位整数
- Int8x16十六个8位整数
- Uint32x4四个无符号的32位整数
- Uint16x8八个无符号的16位整数
- Uint8x16十六个无符号的8位整数
- Bool32x4四个32位布尔值
- Bool16x8八个16位布尔值
- Bool8x16十六个8位布尔值
- Bool64x2两个64位布尔值
每种数据类型都是一个方法,可以传入参数,生成对应的值。
```javascript
var a = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0);
```
上面代码中,变量`a`就是一个128位、包含四个32位浮点数的值。
注意,这些数据类型方法都不是构造函数,前面不能加`new`,否则会报错。
```javascript
var v = new SIMD.Float32x4(0,1,2,3);
// TypeError: SIMD.Float32x4 is not a constructor
```
每种数据类型都有一系列运算符,下面是其中的一些。
- float32x4.abs(v):返回`v`的绝对值
- float32x4.neg(v):返回`v`的绝对值的负值
- float32x4.sqrt(v):返回`v`的平方根
- float32x4.add(v, w)`v``w`对应项的相加
- float32x4.mul(v, w)`v``w`对应项的相乘
- float32x4.equal(v, w):比较`v``w`对应项是否相等,返回的布尔值组成一个`uint32x4`的值
下面是一个`add`运算符的例子。
```javascript
c[i] = SIMD.float32x4.add(a[i], b[i]);
// 或者
var tmp0 = a[i];
var tmp1 = b[i];
var tmp2 = SIMD.float32x4.add(tmp0, tmp1);
c[i] = tmp2;
```
此外,每种数据类型还有操作方法。
`getAt`方法返回指定位置的值。
```javascript
var a = SIMD.float32x4(1.0, 2.0, 3.0, 4.0);
var b = a.getAt(0); // 1.0
```
`zero`方法可以将SIMD值清零。
```javascript
var b = SIMD.float32x4.zero();
```
上面代码中,变量`b`包含的四个32位浮点数全部是0.0。
`shuffle`方法根据指定模式重新生成一个SIMD值。
```javascript
var a = SIMD.float32x4(1.0, 2.0, 3.0, 4.0);
var b = SIMD.float32x4.shuffle(a, SIMD.float32x4.XXYY);
// [1.0, 1.0, 2.0, 2.0]
var c = SIMD.float32x4.shuffle(a, SIMD.float32x4.WWWW);
// [4.0, 4.0, 4.0, 4.0]
var d = SIMD.float32x4.shuffle(a, SIMD.float32x4.WZYX);
// [4.0, 3.0, 2.0, 1.0]
```
下面是一个求平均值的例子。
```javascript
function average(f32x4list) {
var n = f32x4list.length;
var sum = SIMD.float32x4.zero();
for (int i = 0; i < n; i++) {
sum = SIMD.float32x4.add(sum, f32x4list.getAt(i));
}
var total = sum.x + sum.y + sum.z + sum.w;
return total / (n * 4);
}
```
## 二进制数组
SIMD可以与二进制数组结合生成数组实例。
```javascript
var _f64x2 = new Float64Array(_f32x4.buffer);
var _i32x4 = new Int32Array(_f32x4.buffer);
var _i16x8 = new Int16Array(_f32x4.buffer);
var _i8x16 = new Int8Array(_f32x4.buffer);
var _ui32x4 = new Uint32Array(_f32x4.buffer);
var _ui16x8 = new Uint16Array(_f32x4.buffer);
var _ui8x16 = new Uint8Array(_f32x4.buffer);
```
下面是一些应用的例子。
```javascript
// a 和 b 是float32x4数组实例
function addArrays(a, b) {
var c = new Float32x4Array(a.length);
for (var i = 0; i < a.length; i++) {
c[i] = SIMD.float32x4.add(a[i], b[i]);
}
return c;
}
```
## 参考链接
- MDN, [SIMD](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SIMD)
- TC39, [ECMAScript SIMD](https://github.com/tc39/ecmascript_simd)
- Axel Rauschmayer, [JavaScript gains support for SIMD](http://www.2ality.com/2013/12/simd-js.html)

View File

@ -318,6 +318,13 @@ ES7推出了字符串补全长度的功能。如果某个字符串不够指定
'xxx'.padEnd(2, 'ab') // 'xxx' 'xxx'.padEnd(2, 'ab') // 'xxx'
``` ```
如果用来补全的字符串与原字符串,两者的长度之和超过了制定的最小长度,则会截去超出位数的补全字符串。
```javascript
'abc'.padStart(10, '0123456789')
// '0123456abc'
```
如果省略第二个参数,则会用空格补全长度。 如果省略第二个参数,则会用空格补全长度。
```javascript ```javascript