ES7装饰器小记

622 阅读2分钟

前提

Es7 的装饰器借鉴的Python decorator语法糖的写法。这篇写了很久了,也忘了当初是看的哪一位大佬写的demo例子,今天翻出来了老笔记,就决定搬上来先吧,顺便复习下。

引申

如何对装饰器进行封装?(实现的方式)
Object.defineProperty(obj, prop, desc)

  • obj: 目标对象
  • prop: 属性名
  • desc: 针对该属性的描述符

demo-1(作用在类的function上的装饰器)

下面用一个小demo来了解一波装饰器的用法:

首先定义一个装饰器函数readonly,作用为让数据可读

function readonly(tar, key, desc) {
	desc.writable = false; // 数据只读
	return desc;
}

定义一个类,装饰器搭配使用,让该类内的方法只读不可修改

class Dog {
    @readonly   	//实际readonly接受tar参数就是bark.prototype
    bark () {
        return '666';
    }
}

现在实例化一波,试试装饰器readonly作用了没

let dog = new Dog();
dog.bark = '777';

Cannot assign to read only property 'bark' of [object Object] 这边就挂了。

demo背后的实现原理

既然看到了class的语法,那就先说说class怎么用es5实现吧,引申一波:

通常来说,这样就行了

function Dog() {}
Dog.prototype.bark = function() {
    return '666';
}
let dog = new Dog();
dog.bark();       // '666'

装逼点的写法

function Dog () {} 
    
// 第二步修改defineProperty属性来添加一个bark的函数
let desc = {
    value: function () {return '666';},
    enumerable: false,	// 可否枚举
    configurable: true,	// 可否修改/删除
    writable: true,		// 可否用数据运算符进行赋值修改
    };
	
Object.defineProperty (Dog.prototype, 'bark', desc)

回归正题 针对@readonly 装饰器的es5实现

定义一个属性描述符 descriptor

let descriptor = {
    value: function () { return '666' },
    enumerable: false,
    configurable: true,
    writable: true
}
function readonly(tar, key, desc) {
    desc.writable = false; // 数据只读
    return desc;
}
let desc = readonly(Dog.prototype, 'bark', descriptor) || descriptor; // 二选一
Object.defineProperty(Dog.prototype, 'bark', desc);	

demo-2(作用在class的装饰器)

定义类的装饰器函数

function doge(tar) {
    tar.isDoge = true;
}

定义一个class,使用doge装饰器

@doge
class Dog {
}
Dog.isDoge //  true

其实就是修改了defineProperty里面target属性,指向了Dog而不是Dog.prototype。

demo-3(装饰器的变量引入)

通过变量控制decorator的返回值

function doge (par) {
    return function (tar) {
        tar.isDoge = par;
    }
}


@doge(false)   // 控制返回值
class Dog {
}
Dog.isDoge //  false

实际应用场景

react 与 redux 库的结合使用 正常使用

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

使用装饰器

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

注意事项

先来复习一下函数提升的问题

js创建函数的两种方式

function a() {}  // 存在函数提升问题,整个代码块提升到最开始执行
var a = function() {}   // 不存在该问题,与变量提升差不多

若一定要修饰函数,可以用HOC(高阶函数)去处理。

!!! 装饰器不能作用于函数本身,只能作用于类或类的方法上,存在函数提升的问题. 问题链接