ES7特性预览

ES6 都还没得及,现在一些 ES7 的新特性就出来了。得益于Node/Native这种玩意将 JavaScript 发扬光大,所以很多特性是并不是针对浏览器脚本设计的,很多针对 Server 端,或者本地应用。所以浏览器也不会支付所以有 ES6/ES7 的规范。

ES7特性预览

说下名字问题,ES6 规范已经在年中已经正式发布了,发布之后的名字前不是 ECMAScript 6,而是 ECMAScript 2015,本文的标题 ES7 目前的版本是 ECMAScript 2016,正式发布之后叫什么就不知道了,本人对名字什么的不纠结,怎么顺口就怎么叫。即使你叫它 ES6,ES7 也没什么错,它就是ECMAScript第6版,第7版。 主要是 ES6 在没正式发布之前叫ES6 叫习惯了。

废话就这么多,直接进入正题了。

乘方运算符

乘方运算较简单。目前一些语言里面其实以经有了。代码一看就明白:

let squared = 2 ** 2; //  = 2 * 2 = 4
let cubed = 2 ** 3; // = 2 * 2 * 2 = 8

let x = 2;
x **= 3; // x = x**3 = x * x * x = 8;

Array.prototype.includes

这个也好理解,之前一般使用indexOf这个函数的结果是否大于0去判定一个数组里面是否存在一个元素。现在使用includes可以直接返回。相对于indexOf的性能,具体算法上有没有优化只能看各种平台的实现了。

//旧版本实现
if (arr.indexOf(element) >= 0) {
    alert('include element');
}
//新版本实现
if (arr.includes(element)){
    alert('include element');
}

和indexOf一样,支付第二个传数传入起始位置,如:

//旧版本实现,从第5个数开始查找
if (arr.indexOf(element, 5) >= 0) {
    alert('include element');
}
//新版本实现,从第5个数开始查找
if (arr.includes(element, 5)){
    alert('include element');
}

异步方法 & function.sent

最近无论什么语言,都想在并发方面做的更好,ES当然也一样,ES6前,像jQuery,CommonJS ,requireJs等都根据Promise规范自己实现一套Promise(Promise规范分多种,可能依照的规范不同,不多说明),在ES6中定义了规范化的Promise对象(基于Promises A),在ES7中,增加了异步方法更好的进行并发。

async function asyncCall(elem, animations){
  let ret = null;
        try {
            for(const anim of animations) {
                ret = await anim(elem);
            }
        } catch(e) {
            
        }
        return ret;
}

编辑器还没有反应过来,上面的asyncawait都是ES7新增的关键字,定义方法前加上async关键字代表方法是异步,与await同级的还有yield,可以返回一个自增的数据,await表示等待。

对于异步方法,好像很有趣的样子,但由于本人较少接触服务端开发,基本不会用到,所以就没细看了,想了解的可以去传送门详细去了解!

对于 function.sent属性

Object.observe

传送门

从字面上来看,是设计模式里面的观察者模式,从代码编写上来看,是面向切面编程的写法,从功能上来看,最大的做用就是实现AngularJs类似的数据绑定,当然还可以实现其它的。

其它ES6的代理也可以有类似的功能,但不方便功能也没observe强大。使用Object.observe/Object.deliverChangeRecords开启

//定义数据及切面方法
var records;
function observer(recs) {
  records = recs;
}
var obj = { id: 1 };
//将对象“观察”
Object.observe(obj, observer);
obj.a = 'b';
obj.id++;
//删除属性 a
Object.defineProperty(obj, 'a', { enumerable: false });
delete obj.a;
//禁用扩展
Object.preventExtensions(obj);
//开启对象观察(切面)
Object.deliverChangeRecords(observer);
//单元测试
assertChangesAre(records, [
  { object: obj, type: 'add', name: 'a' },
  { object: obj, type: 'update', name: 'id', oldValue: 1 },
  { object: obj, type: 'reconfigure', name: 'a' },
  { object: obj, type: 'delete', name: 'a', oldValue: 'b' },
  { object: obj, type: 'preventExtensions', }
]);
    

Object.values / Object.entries

直接上代码,不难理解

var assert = require('assert');
var values = require('object.values');
var entries = require('object.entries');

var obj = { a : 1, b : 2, c : 3 };
var expected  = { ['a', 1], ['b', 2], ['c', 3] };

var sym = Symbol();
obj[sym] = 4;
obj.d = sym;
expected.push(['d', sym]);

if (!Object.values) {
    values.shim();
}
if (!Object.entries) {
    entries.shim();
}

assert.deepEqual(Object.values(obj), expected);
assert.deepEqual(Object.entries(obj), expected);

语法修复:支持方法参数里面以逗号结尾

这个目前已经有许多JavaScript的实现,支持Json格式,在ES7里面可以应用到方法参数里面:

//比如,有些人喜欢这样写代码:
function method(
    a1,
    a2
    ){
        //TODO
    }
method(
    1,
    2
);

//那么,当方法method扩展了一个参数时,就可以做这样修改
function method(
    a1,
    a2,    //多加一个逗号
    a3     //加入新的参数,不带逗号
    ){
        //TODO
    }
method(
    1,
    2,    //多加一个逗号
    3     //加入新的参数,不带逗号
);

//在ES7里面可以这样了
function method(
    a1,
    a2,    //不管怎么样你都可以带个逗号,而不用担心语法错误,
    ){
        //TODO
    }
method(
    1,
    2,    //当进行参数扩展的时候,直接加入新的扩展参数即可,加入的新的扩展参数也可以带逗号结尾
);

数据类型

谁都知道JavaScript是一个弱类型语言,基本上用 typeof方法只可能出现6种结果,(ES6后又多了symbol),如下:

  • undefined
  • string
  • number
  • object
  • function
  • boolean
  • symbol

其实到了ES7之后也还是这么几种-_-!!, 只不过对于object对象有了一些新的定义。对一些针对性的object的数据结构进一些优化,降低内在占用,提高性能。

主要是内部实现上,优化了以下类型:

  • Structure: (其中之一) uint8, int8, uint16, int16, uint32, int32, float32, float64, any, string, object
  • FieldRecord : (数据字段) { name:属性名, type:TypeDescriptor :类型描述 }
  • Dimensions: (内存空间) 空,或者根据自身数据类型计算。 比如 int8类型就只有1字节

TypeDescriptor 类型描述:

  • Structure: (其中之一) uint8, int8, uint16, int16, uint32, int32, float32, float64, any, string, object
  • Rank:对于简单数据类型都是0,Array类型,就是当前array的深度
  • ArrayDescriptor: 如果是 Array数据,那么这个值又是又是一个 TypeDescriptor 非数组,就是 undefined

所以,一个对象所点用的内存空间就可以为length(Dimensions) = rank of TypeDescriptor

当然,具体实现还要看厂商的了,我们貌似不用关心这些问题。有了数据类型后,会多出一些方法来,还有构造器,由于不常用且没有实例,就看着方法名YY吧,问我我也不知道:

  • AlignTo(value, alignment)
  • IsTypeObject(O)
  • Alignment(typeDescriptor)
  • Size(typeObject)
  • Size(structure, dimensions)
  • Size(structure)
  • OffsetOf(fieldRecords, name?)
  • Opaque(structure)
  • CreateStructTypeDescriptor(structure)
  • CreateArrayTypeDescriptor(typeDescriptor)
  • GetOrCreateArrayTypeDescriptor(typeDescriptor)
  • CreateTypedObjectFromBuffer(arrayBuffer, byteOffset, typeObject)
  • CreateTypedObject(typeObject)
  • Default(typeDescriptor)
  • Coerce(typeDescriptor, value)
  • Initialize(typeDescriptor, dimensions, buffer, offset)
  • ConvertAndCopyTo(typeDescriptor, dimensions, buffer, offset, value)
  • Reify(typeDescriptor, dimensions, buffer, offset, opacity)

ArrayBuffer.transfer

貌似会增加一个ArrayBuffer类。方便处理理数流,

var buf1 = new ArrayBuffer(40);
new Int32Array(buf1)[0] = 42;
 
var buf2 = ArrayBuffer.transfer(buf1, 80);
assert(buf1.byteLength == 0);
assert(buf2.byteLength == 80);
assert(new Int32Array(buf2)[0] == 42);
 
var buf3 = ArrayBuffer.transfer(buf2, 0);
assert(buf2.byteLength == 0);
assert(buf3.byteLength == 0);
 

新的模块导出声明

ES6里面规范了模块的导入导出模式,(非CommonJS和AMD),语法如

目前ES6定义的导入和导出语法如下

导入

语法 导入请求的模块 导入模块里面的变量 本模块对些变量的命名
import v from "mod"; "mod" "default" "v"
import * as ns from "mod"; "mod" "*" "ns"
import {x} from "mod"; "mod" "x" "x"
import {x as v} from "mod"; "mod" "x" "v"
import "mod";

导出

语法 导入的模块 导入的模块里面的变量 本地变量 导出对外的变量名
export var v; null null "v" "v"
export default function f(){}; null null "f" "default"
export default function(){}; null null "*default*" "default"
export default 42; null null "*default*" "default"
export {x}; null null "x" "x"
export {x as v}; null null "x" "v"
export {x} from "mod"; "mod" "x" null "x"
export {x as v} from "mod"; "mod" "x" null "v"
export * from "mod"; "mod" "*" null null

ES7新增语法

语法 导入的模块 导入的模块里面的变量 本地变量 导出对外的变量名
export * as ns from "mod"; "mod" "*" null "ns"
export v from "mod"; "mod" "default" null "v"

主要是增加了从另外一个模块导出,对于代理方法可能会有所简化。

Class 和 Property 使用Decorators(修饰运算)

这个可能看不太懂。什么是修饰运算?,可以为以下几种,不知道用中文怎么表达,直接贴原文了。

  1. an expression
  2. that evaluates to a function
  3. that takes the target, name, and property descriptor as arguments
  4. and optionally returns a property descriptor to install on the target object

比如定义一个属性:

class Person {
  @readonly
  name() { return `${this.first} ${this.last}` }
}

定义一个类:

//在ES6之前
var Foo = (function () {
  function Foo() {
  }
  Foo = F("color")(Foo = G(Foo) || Foo) || Foo;
  return Foo;
})();

//ES6
var Foo = (function () {
  class Foo {
  }

  Foo = F("color")(Foo = G(Foo) || Foo) || Foo;
  return Foo;
})();

//ES7
@F("color")
@G
class Foo {
}

String 字符串相关

规范了String.prototype.trimLeft / trimRight 方法。

这个不用说了,很多厂商自己已经实现了这两个方法。(比如Firefox)

新增加padLeft/padRight

传送门,把字符串向左隔开,或向右隔开:

  • String.prototype.padLeft( maxLength [ , fillString ] )
  • String.prototype.padRight( maxLength [ , fillString ] )

maxLength为最大长度,也是最终长度(当maxLength大于字符串本身长度时),往左或往右填入补充字符串,默认为U+0020(即空格:SPACE)

正则表达式新方法matchAll

  • String.prototype.matchAll 匹配全部符合要求的项。
var regex = /t(e)(st(\d?))/g;
var string = 'test1test2';
string.matchAll(regex); //返回一个遍历对象,类似数组,有[keys / values / entries]信息