lanbos'blog

es6和es7探查

读了《ECMAScript 6 入门》,主要是想谈非常吸引我的两个功能:asyncDecorator,期间会涉及到es6的class相关功能。

终于等来了async

写前端的大多数项目遭遇异步回调地域的情况较少,async主要是解决了nodejs的痛点。从promise到Generator,js终于等来了异步的终结解决方式,就是同步。。。为什么这么说呢,因为:

  1. async写法基本和同步写法一样,无非是加了关键字
  2. 用async写代码很容易让js丧失异步高性能的优势

类同步的写法

1
2
3
4
5
6
var async ReadFile = async function () {
var f1 = await readFile('/etc/fstab');
console.log(f1.toString());
var f2 = await readFile('/etc/shells');
console.log(f2.toString());
};

只需要在外层函数作用域前加async关键词,作用域内异步执行函数加await关键词,就可以像写同步写法一样写异步了。于是出现了在模块全局内直接用一个加了async关键字的立即执行匿名函数包裹整个模块的简单粗暴写法:

1
2
3
4
5
6
7
(async ()=>{
var f1 = await readFile('/etc/fstab');
console.log(f1.toString());
var f2 = await readFile('/etc/shells');
console.log(f2.toString());
//other code...
})();

async函数甚至可以“滥用”,直接给一个非异步执行的函数前加依然是有效的,因为

正常情况下,await命令后面是一个 Promise 对象。如果不是,会被转成一个立即resolve的 Promise 对象。

1
2
3
4
5
6
async function f() {
return await 123;
}

f().then(v => console.log(v))
// 123

所以有了async你可以完全忘记node是一门四处是异步的语言。

容易造成性能损失

nodejs推广最重要的特点之一就是“基于单线程异步的高并发性能io操作”,异步是node刻在血液里的特点,四处异步让coder很容易思考在哪里需要并发,哪里需要继发,保证了cpu性能近少浪费。但是async的到来让异步不在明显,有时无需继发的情况很容易让coder忽略写成同步执行。

多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。

1
2
let foo = await getFoo();
let bar = await getBar();

上面代码中,getFoo和getBar是两个独立的异步操作(即互不依赖),被写成继发关系。这样比较耗时,因为只有getFoo完成以后,才会执行getBar,完全可以让它们同时触发。

1
2
3
4
5
6
7
8
// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);

// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;

上面两种写法,getFoo和getBar都是同时触发,这样就会缩短程序的执行时间。

错误捕捉

如果非说async有什么缺点的话,错误捕捉可能算一个。因为包装了promise,如果promise出现了reject就会报错。只能用try{}catch(){}来进行容错处理。


目前node版本大于7.6的都原生支持了async,可以愉快的使用了。


被angular看重的decorator

前段时间的av大战,a吐槽v一点就是v没有技术贡献,虽然说法偏颇,但是angular的确是很敢于在js上搞大新闻的。新angular(2.x开始的版本)中大量使用了es7新语法decorator.

是否写面向对象的js

在刚接触js的时候面向对象是很困惑的东西,对于部分前端开发者来说其实写基于面向对象写法的js可能是不必要的。js的函数具有高度灵活性,开发者完全可以通过高级函数的特性完成大多数需要面向对象写法的功能。但是高度灵活性带来的问题就是多人配合开发的时候很难统一规范和风格,java之所以经久不衰其中之一的原因也可能是传统面向对象的固定甚至说死板的写法能够束缚所有开发者的编码风格,让开发的沟通成本减少。在当前js承担越来越多的工作,一个js项目需要多人配合,需要更高的代码复用率的趋势下,以往js开发者抵触面向对象的写法的心理逐渐减弱。es6正式引入class语法糖也是官方对面向对象写法的推动,甚至还有微软typescript等让js变的越来越像java的东西不断出现,js新的时代已经来临。

使用

装饰器只能装饰类、类的方法、类的属性。修饰函数的参数在不同情况下代表的意义也不同。
首先定义一个修饰函数作为显示:

1
2
3
4
5
6
function showTest(target, name, descriptor) {
console.log(target);
console.log(name);
console.log(descriptor);
console.log("***");
}

然后简单写一个类,测试修饰函数参数的不同表现

1
2
3
4
5
6
7
8
9
10
11
@showTest
class Test {
@showTest
outName = 1;
@showTest
main() {
// @showTest
this.inName = 2
}
}
let test = new Test();

这里使用的是typescript转译工具把es7转换为es5。
首先发现不能修饰方法里命名的属性,会报错。
注释掉引起错误的代码后控制台输出为(chrome 60):

分析如下

  • 装饰函数第1次执行为装饰类的属性,target属性指向类的实例,name指向属性的key,descriptor是undefined
  • 装饰函数第2次执行为装饰类的方法,target属性指向类的实例,name指向方法的key,descriptor调用了Object.defineProperty()
  • 装饰函数第3次执行为装饰类,target属性指向类本身,name是undefined,descriptor是undefined

注意:在修饰器修饰类时target指向类本身,直接给target增加属性只会给类增加静态属性,实例无法获取,要增添动态属性和方法应给给target.prototype添加,并且增加的方法不应该使用箭头函数。

1
2
3
4
5
6
function showTest(target, name, descriptor) {
target.text1="静态属性实例无法获取";
target.prototype.text="动态属性";
target.prototype.showText=function(){
console.log("不用箭头函数的动态方法");
}

为了优雅

decorator是一个es7针对class出的语法糖,解决的主要问题是优雅。
书中下面代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component({
tag: 'my-component',
styleUrl: 'my-component.scss'
})
export class MyComponent {
@Prop() first: string;
@Prop() last: string;
@State() isVisible: boolean = true;

render() {
return (
<p>Hello, my name is {this.first} {this.last}</p>
);
}
}

语义明确,条例清晰。在写类的时候可以将一些常用的公共逻辑从类中抽离出来(如工具和指令),让类一些功能更语义化。

装饰器和多重继承

es6引入了class关键字,把之前js基于原型链继承的方式包装成为了类似java等传统意义上的面向对象的语言的继承方式。但其本质仍是基于原型链的继承方式,所以继承仍非常灵活,可以用mixin实现多重继承。decorator可以优雅的实现mixin。常用的类的混合:

部署一个通用脚本mixins.js,将 Mixin 写成一个修饰器。

1
2
3
4
5
export function mixins(...list) {
return function (target) {
Object.assign(target.prototype, ...list);
};
}

然后,就可以使用上面这个修饰器,为类“混入”各种方法。

1
2
3
4
5
6
7
8
9
10
11
import { mixins } from './mixins';

const Foo = {
foo() { console.log('foo') }
};

@mixins(Foo)
class MyClass {}

let obj = new MyClass();
obj.foo() // "foo"

总结

asyncDecorator是两个非常超前的js功能,没有获得node和浏览器的广泛支持,甚至Decorator当前只能通过babel和typescript等转换工具来使用。这两个功能并没有给js带来什么功能上非常大的改变,只是让开发者更能优雅的写代码,可以说这两个功能并不是刚需。但是,这两个功能的确是js开发者非常期待的两个功能,尤其是async,可以让node的书写大大提高体验。虽然js并不是一门很年轻的语言,但近年来发力集采其他语言的优点,新加入的功能在python和C#等语言中都能找到熟悉的影子,随着语法和社区的逐渐壮大,越来越多的开发者一定会被js独特的魅力所吸引。