裝飾器(Decorator)是一種設計模式,容許向一個對象添加功能,可是又不改變其內部結構。裝飾器只是ES7中提案,目前處於Stage 2階段,可是不久的未來就會變成規範。它主要用來修飾類以及類屬性。前端
本文總共分爲五個部分:react
@isPerson
class Person {}
function isPerson(target) {
target.prototype.isPerson = true;
}
const person1 = new Person();
console.log(person1.isPerson); // true
複製代碼
上面有一個Person
類,咱們寫了一個isPerson
裝飾器修飾它,最終咱們實例化Person
時,實例上多了isPerson
屬性。git
@isPerson(true)
class Person {}
function isPerson(bol) {
return function(target) {
target.prototype.isPerson = true;
}
}
複製代碼
一樣咱們也能夠給裝飾器isPerson
添加參數,而裝飾器內容就會多一層結構,return
對一個對象。因此裝飾器是支持傳參和不傳參的。es6
本質上能夠把Person
看做一個方法,而實際上裝飾器就是將方法當參數傳入。在Babel編譯我會講解裝飾器的本質。github
isPerson(true)(function Person() {})
複製代碼
class Person {
firstName = 'Peter';
lastName = 'Cheng';
@readonly
realName() { return `${this.firstName} ${this.lastName}` };
}
function readonly(target, name, descriptor) {
descriptor.writable = false;
}
const person2 = new Person();
console.log(person2.realName = 1);
複製代碼
同時給類Person
的realName
方法添加了readonly
裝飾器,當輸出實例的realName
屬性時,程序會報錯,Uncaught TypeError: Cannot assign to read only property 'realName' of object '#<Person>'
。注意,這裏實際上修飾的是類方法,裝飾器目前不能修飾類裏面的變量,好比firstName
和lastName
。設計模式
若是修飾類,那target
就是目標自己,第一個例子中就是Person
類。若是你修飾的是類方法,那target
就是類實例。數組
類名或者類方法名稱,一樣第一個例子,打印出來就是Person
。bash
屬性的描述對象。它具備以下幾個屬性,value
,enumerable
,configurable
,writable
。value是修飾對象自己,而其餘值和Object.defineProperty
的屬性同樣,控制值的行爲。babel
{
value: ƒ realName(),
enumerable: false,
configurable: true,
writable: false
};
複製代碼
裝飾器在React中的應用咱們隨處看見,好比Redux,自定義HOC等,其實這些都是高階函數的應用。app
下面咱們實現一個打點裝飾器,當咱們觸發postClick
方法時,會輸出一個打點的log,最終會輸出postClick 2
。咱們經過攔截value
,並重寫value
,將參數id打印了出來。
class App extends Component {
@analytic()
postClick(id = 1) {}
}
function analytic(args) {
return function decorator(target, name, descriptor) {
if (typeof descriptor === 'undefined') {
throw new Error('@analytic decorator can only be applied to class methods');
}
const value = descriptor.value;
function newValue(...args) {
console.log(`${name} ${args}`);
return value.bind(this)(args);
};
return {
...descriptor,
value: newValue
};
}
}
const app = new App();
app.postClick(2);
複製代碼
咱們選擇修飾類方法的例子,看一下最簡單的裝飾器若是編譯成ES5代碼會是怎麼樣。能夠用Babel官方的網址 babeljs.io/repl,看一下第一個例子中代碼被編譯成什麼樣子。
class Person {
firstName = 'Peter';
lastName = 'Cheng';
@readonly
realName() { return `${this.firstName} ${this.lastName}` };
}
function readonly(target, name, descriptor) {
descriptor.writable = false;
}
const person2 = new Person();
console.log(person2.realName = 1);
複製代碼
編譯以後
function _decorate(decorators, factory, superClass, mixins) {}
// 省略中間一大推
var Person = _decorate(null, function (_initialize) {
var Person = function Person() {
_classCallCheck(this, Person);
_initialize(this);
};
return {
F: Person,
d: [{
kind: "field",
key: "firstName",
value: function value() {
return 'Peter';
}
}, {
kind: "field",
key: "lastName",
value: function value() {
return 'Cheng';
}
}, {
kind: "method",
decorators: [readonly],
key: "realName",
value: function realName() {
return "".concat(this.firstName, " ").concat(this.lastName);
}
}]
};
});
function readonly(target, name, descriptor) {
descriptor.writable = false;
}
var person2 = new Person();
console.log(person2.realName = 1);
複製代碼
其實主要看Person
對象有那些變化,Babel將類編譯成了ES5的function,而且外面套一層裝飾器,可是裝飾器最終仍是賦值給Person
變量。內部Person
對象最終返回一個對象,而key爲realName
的對象有一個decorators
,它是一個數組。咱們看看decorators
作了什麼。其實就是遍歷數組,將參數最終映射到Object.defineProperty
,操做對象的可寫入等屬性。
經過本文咱們知道了裝飾器是什麼,而且用來作什麼,以及實質是什麼。最近看了一部電影《斯隆女士》,裏面就有一個片斷闡述專業性的重要性,做爲前端,首先須要掌握的就是ES相關的知識。終於整理完裝飾器的知識了,我最近正在用Node + Flutter
作一個App,最終計劃是發佈上線,敬請期待。
我用create-react-app生成了一個項目,能夠直接使用裝飾器。github.com/carrollcai/…。
es6.ruanyifeng.com/#docs/decor…
寫做時間: 20190922