JavaScript Decorators

Decorators,即装饰器的意思,是 JavaScript stage-2 阶段的一个草案,它作用于一个类或者类的方法和属性。

由于还处于草案阶段,因此我们使用装饰器还需要通过 babel 并配合 babel-plugin-transform-decorators-legacy 插件来编译。

下面我们来看一下装饰器的一些例子:

function color(target) {
target.color = 'red';
}

@color
class Animal {
}

console.log(Animal.color); // red

在这个例子中 @color 就是一个装饰器,它为 Animal 类添加了一个 color 静态属性,可以让我们通过 Animal.color 直接调用。

但是这里的 color 是写的固定值,接下来我们把它改写成如下代码:

function color(color) {
return function (target) {
target.color = color;
};
}

@color('blue')
class Animal {
}

console.log(Animal.color); // blue

这个时候我们的装饰器就有了传入参数的功能,可以定制想要的效果。

当一个装饰器作用于类的时候,它接收一个参数 target,这个 target 就是被装饰的类本身。其实就相当于一个高阶函数,把被装饰的类传进去进行处理,跟下面的这种写法是等价的:

class Animal {
}

console.log(color('blue')(Animal).color); // blue

装饰器不仅可以作用于类,还可以作用于类的方法或者属性,此时它接收三个参数:类本身的原型对象 target.prototype、属性名 name、该属性的描述对象 descriptor,其实现原理就是通过 Object.defineProperty() 修改类的 descriptor, 下面来看一个把类的属性修改为只读的例子:

function readonly(target, name, descriptor) {
// 可以打印出来查看 descriptor 的详细信息
// console.log(descriptor);
descriptor.writable = false;
return descriptor;
}

class Bird {
@readonly
species = 'bird';

@readonly
say(message) {
return `a brid can say ${message}`;
}
}

let bird = new Bird();

console.log('update before: ', bird.species); // update before: bird
bird.species = 'fish';
console.log('update after: ', bird.species); // update after: bird

console.log('update before: ', bird.say('嘤嘤嘤...')); // update before: a brid can say 嘤嘤嘤
bird.say = function (message) {
return `a fish can say ${message}`;
};
console.log('update before: ', bird.say('嘤嘤嘤...')); // update before: a brid can say 嘤嘤嘤

通过这个例子可以看到,加上 @readonly 装饰器后,虽然我们修改了属性,但是结果还是跟修改前保持一致。

这里通过 Object.defineProperty() 实现的话就是这个样子:

class Bird {
say(message) {
return `a brid can say ${message}`;
}
}

// 定义一个 species 属性、赋值为 bird、只读
Object.defineProperty(Bird.prototype, 'species', {
value: 'bird',
writable: false
});

// 修改 say 属性为只读
Object.defineProperty(Bird.prototype, 'say', {
writable: false
});

看到这里,其实发现 JavaScript 中的装饰器其实就是一个语法糖,能够让我们的代码看起来更加简洁、易懂,写起来也更加方便。