修饰器
修饰器是用来修改类的行为。目前需要 babel 才可以使用。它最大的特点是:可以在编译期运行代码!其本质也就是在编译器执行的函数。其执行格式如下:
1 2 3 4 5 |
@decorator //decorator 是修饰器名,即函数名 class A{} //相当于 class A{} A = decorator(A) || A; |
修饰器函数接受3个参数,依次是目标函数、属性名(可忽略)、该属性的描述对象(可忽略)。
1 2 3 4 5 6 7 8 9 10 |
function test(target){ target.isTestable = true; //利用修饰器给类添加静态属性 target.prototype.isTestable = true; //利用修饰器给类添加动态属性 } @test class A{} console.log(A.isTestable); //true console.log(new A().isTestable); //true |
例如之前的 mixin 可以用修饰器实现一个简单的版本:
1 2 3 4 5 6 7 8 9 10 11 12 |
function mixins(...list){ return function(target){ Object.assign(target.prototype, ...list); } } var Foo = { foo(){console.log("foo");} }; @mixins(Foo) class Cla{} let obj = new Cla(); obj.foo(); //"foo" |
修饰器不仅仅可以修饰类,还可以修饰类的属性和方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function readonly(target, name, descriptor){ descriptor.writable = false; return descriptor; } class Person{ constructor(name, age, tel){ this.name = name; this.id = id; } @readonly id(){return this.id}; } |
当然也可以同时调用2个修饰器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
function readonly(target, name, descriptor){ descriptor.writable = false; return descriptor; } function nonenumerable(target, name, descriptor){ descriptor.enumerable = false; return descriptor; } class Person{ constructor(name, age, tel){ this.name = name; this.id = id; } @readonly @nonenumerable id(){return this.id}; } |
使用修饰器应该注意:虽然类本质是个函数,但修饰器不能用于函数,因为函数具有声明提升。
core-decroators.js
这是个三方模块,使用import {function Namelist} from 'core-decroators';
引入。它提供了几个常见的修饰器:
- @autobind
是对象中的 this 始终绑定原始对象:
1 2 3 4 5 6 7 8 9 10 |
class Person{ @autobind whoami(){ return this; } } let person = new Person(); let getPerson = person.getPerson; getPerson() === person; //true |
- @readonly
使得属性方法只读
1 2 3 4 5 6 |
class Person{ @readonly id = gen(); //gen 是一个计数器 } var p = new Person() p.id = 123; //Cannot assign to read only property 'id' of [object Object] |
- @override
检查子类方法是否正确的覆盖了父类的同名方法,如果不正确会报错
1 2 3 4 5 6 7 |
class Person{ work(){console.log("I am working");} } class Coder extends Person{ @override work(){console.log("I am coding");} //如果不正确会在这里报错 } |
- @deprecate(也作: @deprecated)
在控制台显示一条 warning,表示该方法不久后将被废除,接受一个可选的参数作为警告内容, 接受第二个参数(对象)表示更多信息
1 2 3 4 5 6 7 8 9 10 |
class Person{ @deprecate facepalm(){} @deprecate('We stopped facepalming') facepalmHard(){} @deprecate('We stopped facepalming', {url:'http://balabala.com'}) facepalmHarder(){} } |
- @suppressWarnings
抑制 deprecate 修饰器导致调用 console.warn(), 但异步代码发出的除外。
1 2 3 4 5 6 7 8 9 10 11 12 |
class Person{ @deprecate facepalm(){} @supressWarnings facepalmWithoutWarning(){ this.facepalm(); } } let p = new Person(); p.facepalm(); //控制台显示警告 p.facepalmWithoutWarning(); //没有警告 |
广告买点--日志系统案例代码
传统买点都写在业务逻辑中,为页面中广告做日志统计(点击,展示),展示的时候我们判断一下展示,然后在展示函数中发送一下买点函数
把买点抽离出来可复用的模块,将来接口变了或通讯方式变了,只要改修饰器的代码即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
// Decorator修饰器声名---买点逻辑独立业务 let log=(type)=>{ return function(target,name,descriptor){ let src_method=descriptor.value;//保存一下原始的函数体 // 这个原始方法重新赋值 descriptor.value=(...arg)=>{ src_method.apply(target,arg);//原来的方法先执行 // 模拟一个买点---真实场景中,以下改成自己的代码 console.info(`log ${type}`);//打印log 传入的type } } }; class AD{ @log('show')//买点逻辑 show(){ console.info('ad is show');//模拟业务--对应的是src_method.apply(target,arg); } @log('click')//买点逻辑 click(){ console.info('ad is click');//模拟业务 } }; let newAd = new AD();//实例化类 ad.show();//先执行了'ad is show',在执行了log show ad.click();//同上原理 |