标签(空格分隔): 前端 es6
概念
修饰器(Decorator)是一个函数,用来修改类的行为。这是ES7的一个提案,目前Babel转码器已经支持。
修饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,修饰器能在编译阶段运行代码。
// babel转换依赖插件
{
"plugins": ["transform-decorators-legacy"]
}
基本使用
第一种使用
const test1 = (target) => {
target.key1 = 1 // 扩展静态属性
target.prototype.key1 = 2 // 扩展动态属性
}
test1
class test1Class {}
let test1Obj = new test1Class()
// 结果
test1Class.key1
test1Obj.key1
第二种使用
const test2 = (name) => {
return (target) => {
target.uname = '李云龙' // name是class的关键词,不能使用。
target.prototype.name = name
}
}
@test2('testname')
class test2Class {}
let test2Obj = new test2Class()
// 结果
test2Class.name
test2Class.uname
test2Obj.name
原理
@decorator
class A {}
// 等同于
class A {}
A = decorator(A) || A;
对方法的修饰
额外方法
const readonly = (target, name, descriptor) => {
// descriptor对象原来的值如下
// {
// value: specifiedFunction, // 置换调用
// enumerable: false, // 是否可以枚举(for in 循环能否遍历的到)
// configurable: true, // 是否可以配置(是否可以用delete删除)
// writable: true // 是否可以修改(为false的时候,只是修改没起作用,不会报错)
// };
descriptor.writable = false
return descriptor
}
const configurable = (target, name, descriptor) => {
descriptor.configurable = false
return descriptor
}
const enumerable = (target, name, descriptor) => {
descriptor.enumerable = false
return descriptor
}
class test3Class {
@configurable
@readonly
testKey = {
@enumerable
@configurable
testKey: 1
}
}
let test3Obj = new test3Class()
// test3Obj.testKey = {key: 2}
test3Obj.testKey
// test3Obj.testKey.testKey = 2
for (let key in test3Obj.testKey) {console.log(key)}
delete test3Obj.testKey
test3Obj.testKey.testKey
对value的操作
class Math {
@log
add(a, b) {
return a + b;
}
}
function log(target, name, descriptor) {
var oldValue = descriptor.value;
console.log(oldValue, target)
descriptor.value = function() {
// 这里把console.log改成另外一个监听的add函数,就是一个简单的观察者模式了
console.log(`Calling "${name}" with`, arguments);
return oldValue.apply(null, arguments);
};
return descriptor;
}
const math = new Math();
// passed parameters should get logged now
math.add(2, 4);
为什么decorator不能用于函数
修饰器只能用于类和类的方法,不能用于函数,因为存在函数提升。
var counter = 0;
var add = function () {
counter++;
};
@add
function foo() {
}
等价于
var counter;
var add;
@add
function foo() {
}
counter = 0;
add = function () {
counter++;
};
高级用法(拓展用法)
mixin
const mixins = (...list) {
return function (target) {
Object.assign(target.prototype, ...list);
};
}
const Foo = {
foo() { console.log('foo') }
};
@mixins(Foo)
class MyClass {}
let obj = new MyClass();
obj.foo() // "foo"
通过mixins这个修饰器,实现了在MyClass类上面“混入”Foo对象的foo方法。
不过,上面的方法会改写MyClass类的prototype对象,如果不喜欢这一点,也可以通过类的继承实现mixin。
class MyBaseClass {
foo() { console.log('foo') }
}
class MyClass extends MyBaseClass {
/* ... */
}
另一种写法
let MyMixin = (superclass) => class extends superclass {
foo() {
console.log('foo from MyMixin');
}
};
class MyBaseClass {
foo() { console.log('foo') }
}
class MyClass extends MyMixin(MyBaseClass) {
/* ... */
}
实战
以下截取自bimbox
@Emit() // save-user-profile
saveUserProfile () {
if (this.avatarData) {
this.saveAvatar(this.usernameId, this.avatarData);
}
this.saveTitleCertificate();
}
以下截取自vue-property-decorator
/** vue-property-decorator verson 6.1.0 MIT LICENSE copyright 2018 kaorun343 */
'use strict';
import Vue from 'vue';
import Component, { createDecorator } from 'vue-class-component';
import 'reflect-metadata';
export { Component, Vue };
// Code copied from Vue/src/shared/util.js
var hyphenateRE = /\B([A-Z])/g;
var hyphenate = function (str) { return str.replace(hyphenateRE, '-$1').toLowerCase(); };
/**
* decorator of an event-emitter function
* @param event The name of the event
* @return MethodDecorator
*/
export function Emit(event) {
return function (target, key, descriptor) {
key = hyphenate(key);
var original = descriptor.value;
descriptor.value = function emitter() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
if (original.apply(this, args) !== false)
this.$emit.apply(this, [event || key].concat(args));
};
};
}
网友评论