ES7-Decorator(修饰器) 简单入门

版权声明:本文为博主原创,转载加个原文章链接。 https://blog.csdn.net/xiaolongbaobushibao/article/details/82979146

ES7 Decorator(修饰器) 简单入门

前言

本文有引用阮一峰的例子,侵删。

一、Decorator 是什么

很多语言都有对应的Decorator(修饰器),用来修改原类/方法的功能。比如Java的Annotationy注解也算是修饰器。

不改变原代码内容逻辑的前提下丰富原有功能

class App {
    get state(){
        return 666
    }
    render(){
        console.log("this is App's render func,state is "+ this.state);
    }
}

想在App render的时候前后插入自定义的文字,正常的想法是这样的

class App {
    get state(){
        return 666
    }
    render(){
        console.log("wrap log begin")
        console.log("this is App's render func,state is "+ this.state);
        console.log("wrap log end")
    }
}

如果我有A,B、C App的render都要前后输出自定义文字怎么办?每个都改?万一哪天文字输出方式改变,一个个改吗?这时候修饰器作用就来了


const logWrapper = targetClass =>{
    const orignRender = targetClass.prototype.render;
    targetClass.prototype.render = function(){
        console.log("wrap log begin")
        orignRender.apply(this);//防止this指向改变了
        console.log("wrap log end")
    }
    return targetClass;
}

@logWrapper
class App {
    get state(){
        return 666
    }
    render(){
        console.log("this is App's render func,state is "+ this.state);
    }
}
new App().render();
//输出:
//wrap log begin 
//this is App's render func,state is 666 
//wrap log end

二、为什么要用Decorator

对JavaScript来说应该算是一个语法糖,高阶函数的使用方式。以上例子在不使用Decorator的方式是:


class App {
    get state(){
        return 666
    }
    render(){
        console.log("this is App's render func,state is "+ this.state);
    }
}

const wrapLogApp = logWrapper(App);
new wrapLogApp().render();
//输出:
//wrap log begin 
//this is App's render func,state is 666 
//wrap log end

优点就是看起来直观一点、容易理解、省略了临时变量命名。如果你觉得优点不够明显,那看下的React+Redux例子:

  • 多参数修饰器
class ReactComponent extends React.Component {}

export default connect(mapStateToProps, mapDispatchToProps)(ReactComponent);

村里通电用上修饰器后:

@connect(mapStateToProps, mapDispatchToProps)
export default class MyReactComponent extends React.Component {}
  • 多参数、多重修饰器:
class ReactComponent extends React.Component {}

export default connect(mapStateToProps, mapDispatchToProps)(logWrapper(ReactComponent));

修饰器加成:

@connect(mapStateToProps, mapDispatchToProps)
@logWrapper
export default class MyReactComponent extends React.Component {}

是不是更好理解了?

三、Decorator 的实现与用法

3.1 使用方式

参数

  • 单参数
    修饰方法的第一个参数必定为要修饰的类/方法
const logWrapper = function(targetClass){
    //do something
}
  • 多参数
    除了默认的参数,也可能有修饰方法自己的参数。这时候这个方法需要返回一个方法作为新的修饰方法
const logWrapper = (logText) =>{
    return targetClass =>{
        targetClass.render = () =>{console.log(logText)}
    }
}
//使用方式
@logWrapper("Hello World")
class App{
    //注意,这里没有render方法,通过注入一个render方法
}

new App().render();//输出"Hello World"

返回值

其实上面讲的差不多了,具体的执行步骤大概是这样的

@decorator
class A {}

// 等同于

class A {}
A = decorator(A) || A;

修饰器不一定要返回对象,但如果不返回新对象,那么要对该对象属性进行修改。个人还是推荐有个返回值,也比较好理解,反正你又不亏。

多层级

当一个被修饰的类有多个修饰符,我这里把分成两个阶段:初始化、运行时。

const countWrapper = count =>{
	console.log("修饰初始化:获取修饰方法",count);
	return target => {
		console.log("修饰运行时:修饰方法开始修饰类/方法",count);
		return target;
    }
}
@countWrapper(1)
@countWrapper(2)
class App {

}
//输出
// 修饰初始化:获取修饰方法 1
// 修饰初始化:获取修饰方法 2
// 修饰运行时:修饰方法开始修饰类/方法 2
// 修饰运行时:修饰方法开始修饰类/方法 1
  • 修饰初始化

代码在声明变量的时候,由上而下执行代码块,因此获取修饰方法按照顺序执行:
countWrapper(1)
countWrapper(2)
获得对应的修饰方法

  • 修饰运行时

因为countWrapper(1)需要接受一个参数来修饰,但是参数还没形成,所以等下一个countWrapper(2)修饰完的对象返回值当做1的参数。说的有点绕,拆成非修饰器的实现方式如下:

countWrapper(1)(countWrapper(2)(App))

总之修饰方法对类/方法进行修饰的原则是谁靠的近,谁先执行。

3.1 类的修饰


const uuidWrapper = target => {//给目标类加一个静态变量uuid
    target.uuid = uuid();
}
@uuidWrapper
class App {
    
}

3.2 webpack + Babel

现在很多浏览器还不支持修饰器,用上一些打包工具的时候就可以随心所欲使用修饰器了。
在已经支持ES6语法的前提下:

  • 安装依赖
npm install babel-plugin-transform-decorators-legacy --save-dev
  • 修改babel配置 .babelrc
{
    // 新增一个decorator 插件
    "plugins": ['transform-decorators-legacy']
}


四、注意点

4.1 修饰器不能用于函数

修饰器只能修饰类、类方法。如果直接用于函数会有函数提升的问题,类方法与函数的区别是在类内部不会有函数提升。因此直接用于函数的时候babel插件处理会有错误提示。

关于函数提升:

var counter = 0;

var add = function () {
  counter++;
};

@add
function foo() {
}

意图是执行后counter等于 1,但是实际上结果是counter等于 0;
函数提升后,相当于

@add
function foo() {
}

var counter;
var add;

counter = 0;

add = function () {
  counter++;
};
  • 解决方法:如果一定要修饰函数,可以采用高阶函数的形式直接执行
var counter = 0;

var add = function () {
  counter++;
};

function foo() {
}
const addFun = add(foo)

猜你喜欢

转载自blog.csdn.net/xiaolongbaobushibao/article/details/82979146
今日推荐