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:
parent
0be4b18312
commit
40437de61a
@ -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`代码块中。
|
||||||
|
198
docs/object.md
198
docs/object.md
@ -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`方法。
|
||||||
|
@ -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`方法,也会被拦截。
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
147
docs/simd.md
147
docs/simd.md
@ -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)
|
||||||
|
@ -318,6 +318,13 @@ ES7推出了字符串补全长度的功能。如果某个字符串不够指定
|
|||||||
'xxx'.padEnd(2, 'ab') // 'xxx'
|
'xxx'.padEnd(2, 'ab') // 'xxx'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
如果用来补全的字符串与原字符串,两者的长度之和超过了制定的最小长度,则会截去超出位数的补全字符串。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
'abc'.padStart(10, '0123456789')
|
||||||
|
// '0123456abc'
|
||||||
|
```
|
||||||
|
|
||||||
如果省略第二个参数,则会用空格补全长度。
|
如果省略第二个参数,则会用空格补全长度。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
Loading…
x
Reference in New Issue
Block a user