decorators 参数_Decorators in ES7

这是一篇来自 @lilh42 的投稿。曾经是 Python 程序员的前端,Vim 重度用户,最大的乐趣是用合适的工具解决问题 。

ES6 标准已于上个月(2015年6月17日)正式发布,众多新特性成为标准固然另人激动,然而更值得憧憬的还是未来。所以,让我们来看看 ES7(更正式的说法是 ES2016)有哪些激动人心的变化。最为人津津乐道的可能就是 async/await了,不过我个人非常喜欢 Yehuda Katz 提出的 decorator 模式,本文尝试对此做一个介绍。

decorator 是什么

ES7 的 decorator 概念是从 Python 借来的,在 Python 里,decorator 实际上是一个 wrapper,它作用于一个目标函数,对这个目标函数做一些额外的操作,然后返回一个新的函数:

def my_decorator(fn):

def inner(name):

print 'Hello ' + fn(name)

return inner

@my_decorator

def greet(name):

return name

greet('Decorator!')

# Hello Decorator!

这种 @decorator 的写法其实是一种语法糖,从 my_decorator 的定义就可以看出,它接收一个函数(fn)为参数,定义一个新的内部函数(innner),这个内部函数会定义一些行为,最后 my_decorator 返回这个内部函数(inner)。

上面的 @my_decorator 等于:

greet = my_decorator(greet)

greet('Decorator!')

# Hello Decorator!

ES7 中的 decorator 同样借鉴了这个语法糖,不过依赖于 ES5 的Object.defineProperty 方法 。

关于 Object.defineProperty 的一切(些)

defineProperty 所做的事情就是,为一个对象增加新的属性,或者更改对象某个已存在的属性。调用方式是 Object.defineProperty(obj, prop, descriptor),这 3 个参数分别代表:obj: 目标对象

prop: 属性名

descriptor: 针对该属性的描述符

有意思的是 descriptor 参数,它其实也是一个对象,其字段决定了 obj 的prop 属性的一些特性。比如 enumerable 的真假就能决定目标对象是否可枚举(能够在 for…in 循环中遍历到,或者出现在 Object.keys 方法的返回值中),writable 决定目标对象的属性是否可以更改,等等。完整的描述符可选字段可以参看这里。

作用在方法上的 decorator

先来看一个简单的类:

class Dog {

bark () {

return 'wang!wang!'

}

}

如果我们想让 bark 这个方法成为一个只读的属性,那么可以定义一个readonly 的 decorator:

// 注意这里的 `target` 是 `Dog.prototype`

function readonly(target, key, descriptor) {

descriptor.writable = false

return descriptor

}

可以看到,decorator 就是一个普通函数,只不过它接收 3 个参数,与Object.defineProperty 一致。具体在这里,我们就是把 descriptor 的writable 字段设为 false。

然后把 readonly 作用到 bark 方法上:

class Dog {

@readonly

bark () {

return 'wang!wang!'

}

}

let dog = new Dog()

dog.bark = 'bark!bark!'

// Cannot assign to read only property 'bark' of [object Object]

@readonly 具体做了什么呢?我们先来看一下 ES6 的 class 在转换为 ES5 代码之后是什么样的,即 Dog 这个 class 等价于:

// 步骤 1

function Dog () {}

// 步骤 2

Object.defineProperty(Dog.prototype, 'bark', {

value: function () { return 'wang!wang!' },

enumerable: false,

configurable: true,

writable: true

})

对 bark 方法应用 @readonly 之后,JS 引擎就会在进行步骤二之前调用这个 decorator:

let descriptor = {

value: function () { return 'wang!wang!' },

enumerable: false,

configurable: true,

writable: true

}

// decorator 接收的参数与 Object.defineProperty 一致descriptor = readonly(Dog.prototype, 'bark', descriptor) || descriptor

Object.defineProperty(Dog.prototype, 'bark', descriptor)

所以,ES7 的 decorator,作用就是返回一个新的 descriptor,并把这个新返回的 descriptor 应用到目标方法上。稍后我们将会看到,decorator 并非只能作用到类的方法/属性上,它还可以作用到类本身。

作用在类上的 decorator

作用在方法上的 decorator 接收的第一个参数(target)是类的 prototype;如果把一个 decorator 作用到类上,则它的第一个参数 target 是类本身:

// 这里的 `target` 是类本身function doge (target) {

target.isDoge = true

}

@doge

class Dog {}

console.log(Dog.isDoge)

// true

decorator 也可以是 factory function

如果我们想对不同的目标对象应用同一个 decorator,但同时又需要有一些差别,怎么办?很简单:

function doge (isDoge) {

return function(target) {

target.isDoge = isDoge

}

}

@doge(true)

class Dog {}

console.log(Dog.isDoge)

// true

@doge(false)

class Human {}

console.log(Human.isDoge)

// false

对方法来说也是类似的:

function enumerable (isEnumerable) {

return function(target, key, descriptor) {

descriptor.enumerable = isEnumerable

}

}

class Dog {

@enumerable(false)

eat () { }

}

decrator 能做什么

在 Python 里,decorator 的作用非常丰富,比如可以在使用 threading 时候简化分配锁和解锁的步骤,进行用户认证,定义一些快捷方式(如@staticmethod,@classmethod 之类),定义后端 api 的路由(如 Flask 框架)等等。

对 ES7 来说,现在已经有人写了一个 core-decorators,提供了一些非常实用的 decorator。

比如 @deprecate:

import { deprecate } from 'core-decorators'

class Dog {

@deprecate

peeInRoom () {}

@deprecate('I am a good dog.')

peeInBed () {}

}

let dog = new Dog()

dog.peeInRoom()

// DEPRECATION Dog#peeInRoom: This function will be removed in future versions.

dog.peeInBed()

// DEPRECATION Dog#peeInBed: I am a good dog.

除此之外,raganwald(JavaScript Allongé, the “Six” Edition 的作者)写了一篇文章介绍如何使用 decorator 来实现 mixin。这对框架和类库作者来说是一个重磅好消息。

现在就想用?

decorator 目前还只是一个提议,但是,感谢 Babel,我们现在就可以体验它了。首先,安装 babel:

npm install babel -g

然后,开启 decorator:

babel --optional es7.decorators foo.js > foo.es5.js

babel 也提供了一个在线的 REPL,勾选 experimental 选项,就可以了。

总结

decorator 让我们有机会在代码的执行期间改变其行为,我相信它在 Python 中可以做到的事情,在 ES7 中也同样能够做到。

参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值