写在最前:本文转自掘金
我们平常开发中或多或少的听说使用过装饰器,也切身感受到它带给我们的遍历。本文将聚焦ts的装饰器,去探讨什么是装饰器,如何使用。
js原生目前不支持装饰器,只能通过babel体验装饰器这个新特性。
装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,访问符,属性或参数上。装饰器使用@expression这种形式,expression求职后必须为一个函数,它会再运行时被调用,被装饰的声明信息作为参数传入。
由于装饰器目前还是实验中的特定,在js中处于stage-3阶段。在ts中已经作为一项实验性予以支持。开启装饰器需要在tsconfig.json文件中启用 experimentalDecorators 编译器选项。
类装饰器是我们最常使用到的,它的通常作用是,为该类扩展功能
设想有这样一个场景。目前有一个Tank类,有一个Plane类,有一个Animal类。这三个类都需要一个公共的方法来获取他们所在的位置。我们第一可能想到使用继承来实现。
class BaseClass {
getPosition() {
return {
x: 100,
y: 200,
z: 300,
}
}
}
class Tank extends BaseClass{}
class Plane extends BaseClass {}
class Animal extends BaseClass {}
这样三个类都可以调用getPosition方法来获取各自的位置了。到目前为止看起来没有什么问题。
现在又有新需求,Tank类和Plane类需要一个新的方法addPetrol来给坦克和飞机加油。而动物不需要加油。此时这种写法好像不能继续进行下去了。而js目前没有直接语法提供多继承的功能,我们的继承方向好像行不通了。这个时候类装饰器可以很完美的实现这样的功能。
装饰器功能之一——能力扩展
我们把getPosition和addPertrol都抽象成一个单独的功能,它们得作用是给宿主扩展对应的功能。
const getPositionDecorator: ClassDecorator = (constructor: Function) => {
constructor.prototype.getPosition = () => {
return [100, 200]
}
}
const addPetrolDecorator: ClassDecorator = (constructor: Function) => {
constructor.prototype.addPetrol = () => {
// do something
console.log(`${constructor.name}进行加油`);
}
}
@addPetrolDecorator
@getPositionDecorator
class Tank {}
@addPetrolDecorator
@getPositionDecorator
class Plane {}
@getPositionDecorator
class Animal {}
这样的话,假如日后我们有其他的需求,都可以对他进行能力扩展,让其具有加油的能力。
注意,多个装饰器叠加的时候,执行顺序为离被装饰对象越近的装饰器越先执行。
装饰器功能之二——重载构造函数
在类装饰器中如果返回一个值,它会使用提供的构造函数来替换类的声明。
function classDecorator<T extends {new(...args:any[]):{}}>(constructor:T) {
return class extends constructor {
newProperty = "new property";
hello = "override";
}
}
@classDecorator
class Greeter {
property = "property";
hello: string;
constructor(m: string) {
this.hello = m;
}
}
方法装饰器接收三个参数:
装饰器功能之一——能力增强
我们代码编写时,经常会做一些错误catch,使用装饰器对每个方法进行增加,使它们自动获取catch错误的能力~
const ErrorDecorator: MethodDecorator = (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {
const sourceMethod = descriptor.value;
descriptor.value = async function (...args: any) {
try {
await sourceMethod.apply(this, args);
} catch (error) {
console.error('捕获到了错误');
// do something
}
}
}
class MusicSystem {
getMusicById(name: string): Promise<{name: string, singer: string}> {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.round(Math.random())) {
resolve({name: '凤凰传奇', singer: '玲花|曾毅'});
} else {
reject()
}
}, 1000);
})
}
@ErrorDecorator
async play(name: string) {
const music = await this.getMusicById(name);
// ... do something
console.log(`在曲库中找到了名为${music.name}的音乐,由${music.singer}进行演唱,敬请欣赏。`);
}
@ErrorDecorator
async deleteByName(name: string) {
const music = await this.getMusicById(name);
// ... do something
console.log(`${music.name}音乐删除成功!`);
}
}
const musicSystem = new MusicSystem();
musicSystem.play('凤凰传奇');
musicSystem.deleteByName('凤凰传奇');
细心的同学可以发现了,我们在方法装饰器中无法捕获到实际的错误,比如精准报错哪首歌没找到。很遗憾,目前装饰器的原生能力,是无法获取到我们调用时候传入的具体参数的。因为装饰器实在编译阶段执行的。但是,我们可以通过其他方式实现这样的功能,这就是大名鼎鼎的 metadata 。我们会在文章的末尾提到它。
装饰器功能之一——descriptor修改
通过修改descriptor,我们可以实现对方法进行重新描述。比如设置方法禁止修改,禁止删除等。
const DescriptorDecorator: MethodDecorator = (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) : object => {
return {
value: () => {
console.log('eat方法被替换')
},
writable: true,
enumerable: true,
configurable: true,
};
}
class Pig {
name = 'peiqi';
@DescriptorDecorator
eat() {
}
}
同样的,也可以直接对descriptor进行修改
descriptor.value = () => {console.log('eat方法被替换')};
descriptor.writable = true;
descriptor.enumerable = true;
descriptor.configurable = true;
方法装饰器的使用方式很多,大多数的使用方式是对descriptor的value属性进行替换,拦截等实现功能。
【下边的三个装饰器类型,相对来说使用比较少,有兴趣的小伙伴可以查看原文】
前面一篇关于智能合约翻译文讲到了,是一种计算机程序,既然是程序,那就可以使用程序语言去编写智能合约了。而若想玩区块链上的项目,大部分区块链项目都是开源的,能看得懂智能合约代码,或找出其中的漏洞,那么,学习Solidity这门高级的智能合约语言是有必要的,当然,这都得在公链``````以太坊上,毕竟国内的联盟链有些是不兼容Solidity。Solidity是一种面向对象的高级语言,用于实现智能合约。智能合约是管理以太坊状态下的账户行为的程序。Solidity是运行在以太坊(Ethereum)虚拟机(EVM)上,其语法受到了c++、python、javascript影响。Solidity是静态类型
我正在为我的在线商店使用SpreeCommerce。我想在结帐过程中更改一些行为,这些行为在spreegem内的app/models/spree/order/checkout.rb中定义。所以我在我的应用程序中的同一点创建了一个checkout_decorator.rb。问题是,我的更改没有加载。另一个问题是,模块内的所有内容都在一个方法内,即defself.included(klass)方法。所以我想我必须覆盖整个文件,而不是只覆盖一种方法。这是我的装饰器的样子:checkout_decorator.rbSpree::Order::Checkout.module_evaldodefs
Halo,这里是Ppeua。平时主要更新C语言,C++,数据结构算法,Linux…感兴趣就关注我吧!你定不会失望。目录1.ls显示当前目录下的文件内内容2.pwd-显示用户当前所在的目录3.cd-改变工作目录。将当前工作目录改变到指定的目录下1.cd-回到上一次待的工作空间2.cd..返回上一层目录1.相对路径:cd../aurora2.绝对路径:cd/home/aurora/lesson1/aurora3.cd~进入用户家目录4.cd/进入root目录4.mkdir-新建目录5.rmdir/rm-删除1.rmdir删除空文件夹2.rm删除1.rm-f2.rm-i3.rm-r1.ls显示当前目
我这样使用ruby记录器:$logger=Logger.newMultiIO.new($stdout,log_file)MultiIO是我从thisanswer得到的一个类.这在大多数情况下效果很好,但我正在使用'colored'rubygem在终端上提供彩色输出。不幸的是,这也最终出现在日志文件中,因为ANSI转义看起来像[32mPASS[0m或一些类似的不可打印字符垃圾。清理日志文件字符串同时保持tty字符串颜色的最佳方法是什么?我不介意猴子修补Logger或MultiIO,但我绝对不希望对日志文件和屏幕进行两次不同的调用。 最佳答案
我今天从Python的角度学习Ruby。我完全没能解决的一件事是装饰器的等价物。为了精简内容,我尝试复制一个简单的Python装饰器:#!/usr/bin/envpythonimportmathdefdocument(f):defwrap(x):print"Iamgoingtosquare",xf(x)returnwrap@documentdefsquare(x):printmath.pow(x,2)square(5)运行这个给我:Iamgoingtosquare525.0因此,我想创建一个函数square(x),但要对其进行装饰,以便它在执行之前提醒我它要对什么进行平方。让我们去掉糖
我目前正在使用一些旧版JavaScript开发一个项目。该应用程序不包含模块加载器,它只是将所有内容作为全局变量放入window对象中。遗憾的是,接触遗留代码并包含模块加载器对我来说不是一个可行的选择。我想在我自己的代码中使用typescript。我设置了typescript编译器选项module:"none"在我的tsconfig.json中,我只使用命名空间来组织我自己的代码。到目前为止效果很好。..到现在为止:import*asRxfrom'rxjs';..Rx.Observable.from(['foo',bar']);...//ResultsinTypeScript-Erro
我有密码letz;z=50;z='z';我的tsconfig.json是:{"compilerOptions":{"target":"es5","module":"commonjs","sourceMap":false,"noEmitOnError":true,"strict":true,"noImplicitAny":true}}但是编译成js没有异常是什么鬼?最好的问候,克罗瓦 最佳答案 因为z永远不会被输入为any。z的类型只是根据您分配给它的内容进行推断。来自releasenotes:WithTypeScript2.1,in
我正在研究Angular库并寻找一种使用装饰器模式扩展指令的方法:angular.module('myApp',[]).decorator('originaldirectiveDirective',['$delegate',function($delegate){varoriginalLinkFn;originalLinkFn=$delegate[0].link;return$delegate;}]);使用此模式扩充原始指令的最佳方式是什么?(示例用法:在不直接修改其代码的情况下对指令进行额外的监视或额外的事件监听器)。 最佳答案
在view.js文件中:constcanvas=document.getElementById('canvas');...export{canvas,};在main.js文件中:import*asviewfrom'../src/view.js';...xPosition:view.canvas.width/2,给我'属性'width'在类型'HTMLElement'上不存在。类型检查错误。我不知道如何进行,我对typescript的了解为零,而且程序是用javascript编写的。我读过的所有解决方案都需要使用typescript,这在这个例子中是没有用的。有什么办法可以消除这个错误吗
我正在开发一个网络应用程序,使用angular1.5、typescript2.4.0、moment:2.18.1和gulp进行项目组装。这是我的tsconfig.json:{"files":["src/app/main.ts","types/**/*.ts"],"compilerOptions":{"noImplicitAny":false,"target":"es2015","allowSyntheticDefaultImports":true}}在我的date-range-picker.component.ts中。我正在导入时刻库,正如maindocumentation中所建议的那