此文已由做者吳佳祥受權網易雲社區發佈。
html
歡迎訪問網易雲社區,瞭解更多網易技術產品運營經驗。vue
好吧我認可這是篇任務。git
最近看到個消息,ES2017已經定稿了,心想,我去,還徹底沒了解ES2016呢,ES8就定稿了,out了,這可咋辦,趕忙Google(Baidu)去!github
不過從ES6(2015)以後,tc39的規劃是一年一個版本,因此ES7跟ES8也不會像ES6那麼大的步子。粗略瞟了一眼,咦,裝飾器(Decorator)還沒到 Stage 3啊,好吧不過已經到了2了,想必以後仍是會慢慢歸入的,就先了解一下吧。與之相關的,就是AOP(面向切面編程)和裝飾器模式了。編程
AOP是對OOP(面向對象編程)的一個橫向的補充,主要做用就是把一些業務無關的功能抽離出來,例如日誌打印、統計數據、數據驗證、安全控制、異常處理等等。這些功能都與某些核心業務無關,但又隨處可見,若是都是複製粘貼未免太沒逼格,並且難以維護,不優雅。把它們抽離出來,用「動態」插入的方式嵌到各業務邏輯中。這樣的好處是業務模塊能夠變得比較乾淨,不受污染,同時這些功能點可以獲得很好的複用,給模塊解耦。安全
因爲語言的特性,在JavaScript中能夠輕鬆地實現AOP技術。
app
讓咱們假設一個業務無關的功能--綁定變量:函數式編程
有三個變量a、b、c,要保證b、c在修改先後,a一直等於b與c的和。函數
先分析一下,此處的業務無關功能點(即切面)應該就是「a一直等於b與c的和」。即在業務邏輯走完以後,須要從新將b+c賦值給c;同時,咱們還應該保證賦給b與c的值應該都是Number類型。this
首先,咱們先來個ES3版本均可以兼容的辦法,使用裝飾器模式來實現AOP。
什麼叫裝飾器模式?即提供一個和原功能同樣調用方法的裝飾器,裝飾器裏邊植入了切面。因爲在JS中函數是一等公民,因此咱們能夠提供一個裝飾器函數來實現AOP。
var a = b = c = 0; function setA (action) { return function (value) { action(value); a = b + c; }; } function validateValue (action) { return function (value) { if (typeof value !== 'number') { throw new Error('你傳了個什麼鬼進來'); } action(value); }; } var setB = validateValue(setA(function (value) { b = value; })); var setC = validateValue(setA(function (value) { c = value; })); setB(10); // a === 10; setC(1); // a === 11; setC('什麼鬼'); // Uncaught Error: 你傳了個什麼鬼進來
用裝飾器函數來實現AOP在實際的編程中仍是挺有用的,有利於把邏輯劃分紅更小粒度的模塊,同時也符合函數式編程(FP)的思想。
但是這個方法其實有個噁心的地方,我不想把賦值的「=」用函數來代替啊,腫麼破?
難(tao)過(yan)的是,JS中沒有提供運算符重載的功能。不過ES5中提供了一個新的API可讓咱們實現重載「=」運算符-- Object.defineProperty 以及 Object.defineProperties ,相關用法能夠點擊連接查看。因爲這個API的操做對象是一個Object,因此咱們能夠把a、b、c三個變量包在一個對象中。
var accessorDecorator = (function () { var context = {b:0,c:0}; return { set: function (action) { return function (value) { action.call(this, value); wrapper.a = this.b + this.c; }.bind(this); }.bind(context), get: function (action) { return action.bind(context); } }; })(); var wrapper = Object.defineProperties({}, { a: { value: 0, writable: true }, b: { set: validateValue(accessorDecorator.set(function (value) { this.b = value; })), get: accessorDecorator.get(function () { return this.b; }) }, c: { set: validateValue(accessorDecorator.set(function (value) { this.c = value; })), get: accessorDecorator.get(function () { return this.c; }) } }); function validateValue (action) { return function (value) { if (typeof value !== 'number') { throw new Error('你傳了個什麼鬼進來'); } action(value); }; } wrapper.b = 10; // wrapper.a === 10; wrapper.c = 1; // wrapper.a === 11; wrapper.c = '什麼鬼'; // Uncaught Error: 你傳了個什麼鬼進來
須要說起的一點是,如今很是流行火熱的Vue.js的數據綁定原理就是經過這個實現的。
在文章的開頭,還提到了新的ES草案--裝飾器語法(Decorator),事實上,這也算是 Object.defineProperty 的一個語法糖。使用裝飾器,可讓咱們上面的代碼變得更簡潔(好吧這很JAVA)。
class Wrapper { a = 0; @validateValue @setA b = 0; @validateValue @setA c = 0; }function validateValue (target, key, descriptor) { const action = descriptor.set; descriptor.set = (value) => { if (typeof value !== 'number') { throw new Error('你傳了個什麼鬼進來'); } action(value); }; }function setA (target, key, descriptor) { const action = descriptor.set; descriptor.set = (value) => { action(value); target.a = target.b + target.c; }; }let wrapper = new Wrapper;
更JAVA的是,ES6中提供了 Proxy 與 Reflect 對象。因此咱們的這段代碼如今能夠這麼寫:
let validateProxy = new Proxy({a: 0, b: 0, c: 0}, { set(target, key, value, receiver) { if (key in target && typeof value !== 'number') { throw new Error('你傳了個什麼鬼進來'); } return Reflect.set(target, key, value, receiver); } }); let wrapper = new Proxy(validateProxy, { set(target, key, value, receiver) { let done = Reflect.set(target, key, value, receiver); if (key === 'b' || key === 'c') { Reflect.set(target, 'a', target.b + target.c, receiver); } return done; } });
隨着ECMAScript的新標準的定稿,AOP的實如今JavaScript中是愈來愈容易了。在實際編碼中使用AOP和裝飾器模式,能夠將一些業務無關的代碼從業務邏輯中抽離出來,使得業務邏輯更加清晰,不受污染,同時也有利於這些業務無關代碼的複用與維護。
網易雲免費體驗館,0成本體驗20+款雲產品!
更多網易技術、產品、運營經驗分享請點擊。
相關文章:
【推薦】 一個體驗好的Windows 任務欄縮略圖開發心得