摘要:從裝飾模式到裝飾器裝飾模式裝飾模式的作用是在不修改原有的接口的情況下,讓類表現的更好。它是一個語法糖說完了裝飾模式,我們再看一下在中最新引入的裝飾器。
從裝飾模式到裝飾器 裝飾模式
裝飾模式的作用是:在不修改原有的接口的情況下,讓類表現的更好。
什么叫更好?
自然是繼承有一些問題
繼承會導致超類和子類之間存在強耦合性,當超類改變時,子類也會隨之改變;
超類的內部細節對于子類是可見的,繼承常常被認為破壞了封裝性;
js中的裝飾模式實際上把一個對象A嵌入到另外一個對象B中,也就是B對象包裝了A對象,形成了一個包裝鏈。請求隨著這一條包裝鏈一次傳遞到了包裝鏈上的所有對象,每一個對象都有機會處理這一個請求。
設想一下場景,打農藥,我選了一個后羿,然后開始一級我學了第一個技能。
var HouYi = { skill : function() { console.log("增加了攻速") } HP :function(){ return 1999 } } HouYi.skill()
結果,自以為自己很6了的后羿去了下路,遇到了狄仁杰,二馬一錯蹬幾個回合下來就后羿就涼了。后羿覺得不服,回家一趟,發奮努力,又學了第二個技能和第三個技能。
class Hero { buy(){ console.log("有了一雙鞋子") // console.log("有了一件復活甲") // console.log("有了一把飲血劍") } } var Houyi = new Hero() Houyi.buy()
那么問題來了,我們看了一下,后羿還是回家買了新的武器裝備,而不是寒酸的只有一雙鞋。但是,我們看到,每次后羿買東西都要回家,也就是都要修改buy方法,那么怎么樣在不回家,不修改buy的方法的基礎上又把東西賣了呢,也就是,如何動態的買東西。
從英雄聯盟過渡到王者榮耀。
也就是在代碼運行期間,我們很難切入某個函數的執行環境。
class Hero { buyShoes(){ console.log("有了一雙鞋子") } } var Houyi = new Hero() var buyAtk = function() { console.log("有了一把飲血劍") } var buyDef = function () { console.log("有了一件復活甲") } var buyShoes= Houyi.buy Houyi.buybuybuy = function() { buyShoes() buyAtk() buyDef() } Houyi.buybuybuy()
總結一下:裝飾模式是為已有功能動態地添加更多功能的一種方式,把每個要裝飾的功能放在多帶帶的函數里,然后用該函數包裝所要裝飾的已有函數對象,因此,當需要執行特殊行為的時候,調用代碼就可以根據需要有選擇地、按順序地使用裝飾功能來包裝對象。優點是把類(函數)的核心職責和裝飾功能區分開了。
裝飾模式的缺點:缺點的話我們也能看到我們定義了很多很相似的細小對象到我們的命名空間中,這樣使我們的架構變得十分的復雜,窮于管理。這就有可能導致,我們不是使用它而是被它使用。
它是一個語法糖說完了裝飾模式,我們再看一下在ES7中最新引入的裝飾器(decorator)。這個概念其實是從python里引進的。
def my_decorator(fn): def inner(name): print "Hello " + fn(name) return inner @my_decorator def greet(name): return name greet("Decorator!") # Hello Decorator!
這種@decorator的寫法其實就是一個語法糖。
語法糖意指那些沒有給計算機語言添加新功能,而只是對人類來說更“甜蜜”的語法。語法糖往往給程序員提供了更實用的編碼方式,有益于更好的編碼風格,更易讀。不過其并沒有給語言添加什么新東西。
所以,ES7中的 decorator 同樣借鑒了這個語法糖,不過依賴于ES5的Object.defineProperty 方法 。
defineProperty 所做的事情就是,為一個對象增加新的屬性,或者更改對象某個已存在的屬性。調用方式是 Object.defineProperty(obj, prop, descriptor) ,這 3 個參數分別代表:
obj: 目標對象
prop: 屬性名
descriptor: 針對該屬性的描述符
關于descriptor代表的意思是對象描述符,它本身一個對象,用于描述目標對象的一個屬性的屬性。
value:屬性的值,默認為undefined
writable:能否修改屬性的值,默認值為true
enumerable:能否通過for-in循環返回屬性。默認為ture
configurable:能否通過delete刪除屬性從而重新定義屬性,能否修改屬性的特性,能否把屬性修改為訪問器屬性,默認為true.
關于最后一個configurable還要多說一句,當我們把對象的一個屬性設置為false之后,我們就不能再二次修改了,也就是不能置成true,會報錯。
const object1 = {}; Object.defineProperty(object1, "property1", { value: 42, writable: false, configurable:false }); object1.property1 = 77; // throws an error in strict mode console.log(object1.property1); // expected output: 42裝飾類
decorator大量用于裝飾ES6中的類。具體的寫法是:
@decoratorFun class Base{} function decoratorFun(target){ target.bool = true } Base.bool // true
上面的代碼,decorator就是一個裝飾器,它裝飾了Base這個類的行為(為其增加了一個靜態屬性)。而函數的target屬性,就是被裝飾的類本身。
所以,就是說,如果裝飾器是一個對類做修改裝飾的一般函數,那么他的第一個參數就是被裝飾類的引用。但是,如果一個參數不夠用,怎么辦?
如果不夠用,那么直接可以在外面再包一層。
function decorFun2(str){ return function(target){ target.name = str } } @decorFun2("zhangjingwei") class Person {} Person.name // zhangjingwei @decorFun2("susu") class Person2 {} Person2.name // susu
class Foo1 { classMethod() { return "hello"; } } class Foo2 { static classMethod() { return "hello"; } } Foo1.classMethod() var foo1 = new Foo1(); foo1.classMethod() Foo2.classMethod() var foo2 = new Foo2() foo2.classMethod()
靜態屬性和實例屬性。靜態屬性是類本身的屬性,類生成的對象不能繼承該屬性,但是實例屬性是類生成的對象可以繼承的。
ES6的規范說,類里面沒有靜態屬性,只有靜態方法。
上面的都是給類添加靜態屬性,如果想要增加實例屬性,那么可以操作類的原型。
function decorFun3(name){ return function(target) { target.prototype.name = name } } @decorFun3("lubanqihao") class Nongyao {} let ny1 = new Nongyao() ny1.name // lubanqihao裝飾類的屬性
裝飾器不僅可以裝飾類,還能裝飾類的方法。
有的時候,我們想把類中的某個屬性設置成只讀不支持修改,可以來用裝飾器來實現。
function readonly(target,name,descriptr){ descriptor.writable = false return descriptor } class Cat{ @readonly say(){ console.log("miaomiao) } } let kitty = new Cat() kitty.say = function(){ console.log("wangwang") } kitty.say() //miaomiao
我們看到通過裝飾器給類中的say方法,設置成了只讀。
參數有三個,target name descriptor。
第一個參數是類的原型對象,裝飾器的本意是裝飾類的實例,但是因為類的實例還沒有生成,只能去修飾類的原型。第二個參數是要修飾的屬性名。第三個參數是該屬性的描述對象。
這個很眼熟是不是?
是不是有點類似于Object.defineProperty().其實裝飾器對類的屬性的作用,就是通過Object.defineProperty這個方法進行擴展和封裝。
實際上裝飾器的行為原理是這樣的:
let descriptor = { value:function(){ console.log("miaomiao) }, enumerable:false, configable:true, writable:true } descriptor = readonly(Cat.protitype,"say",descriptor) || descriptor Object.defineProperty(Cat.prototype, "say",descriptor);
所以,我們看到,當裝飾器操作類本身的時候,操作的對象也是類本身,但裝飾器操作的是類中的方法的時候,操作的對象是是類的描述符descriptor,因為類中的屬性的全部信息都記錄在這個描述符里面。
裝飾器不能修飾方法,是因為存在變量提升,不會生效
core-decorators.js第三方模塊core-decorators.js是一個第三方模塊,提供了一些常用的裝飾器。
@autobind:這個裝飾器的作用是自動綁定this對象。
import {autobind} from "core-decorators"; class Person { @autobind getPerson() { return this } } let p1 = new Person() let getPerson = p1.getPerson() getPerson() === p1 // true
@readonly 修飾方法使方法變得不可寫
import {readonly} from "core-decorators" class Dog { @readonly name = "zhang" } let d1 = new Dog() d1.name = "susu" // 會報錯,因為屬性不可寫
override 會檢查子類的方法是否覆蓋了父類的同名方法。如果不正確,會報錯。
import {override} from "core-decorators" class Parent { speak(first, second) {} } class Child extends Parent { @override speak() {} // SyntaxError: Child#speak() does not properly override Parent#speak(first, second) } // or class Child extends Parent { @override speaks() {} // SyntaxError: No descriptor matching Child#speaks() was found on the prototype chain. // // Did you mean "speak"? }Mixin混入
意思就是在一個對象中混入另外一個對象的方法,是對象繼承的一種替代方式。
//一個混入的簡單實現: class Foo { foo() { console.log("hahaha") } } class Bar {} Object.assign(Bar.prototype,Foo) let bar1 = new Bar() bar1.foo()
上邊的例子是通過Object.assign方法進行的。Bar的實例都有foo方法。
所以呢,我們可以把混入的方法多帶帶拿出來,結合裝飾器使用。
// mixin.js export function mixin(...list){ return function (target) { Object.assign(target.prototype,...list) } }
import {mixin} from "./mixin.js class Foo { foo() { console.log("lalala") } } @mixin(Foo) class NewFoo {} let nf = new NewFoo() nf.foo()
這樣一個不好的地方,就是他會改寫新類的原型對象。
Babel轉碼需要安裝下邊的東西:
npm install --save-dev babel-core babel-preset-stage-0
然后設置文件.babelrc
{ "plugins": ["transform-decorators"] }
這樣,就能在代碼里實現裝飾器了。
參考資料https://github.com/zhiqiang21...
http://www.liuhaihua.cn/archi...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/90411.html
摘要:本文從裝飾模式出發,聊聊中的裝飾器和注解。該函數的函數名。不提供元數據的支持。中的元數據操作可以通過包來實現對于元數據的操作。 ??隨著Typescript的普及,在KOA2和nestjs等nodejs框架中經常看到類似于java spring中注解的寫法。本文從裝飾模式出發,聊聊Typescipt中的裝飾器和注解。 什么是裝飾者模式 Typescript中的裝飾器 Typescr...
摘要:什么是裝飾器模式向一個現有的對象添加新的功能,同時又不改變其結構的設計模式被稱為裝飾器模式,它是作為現有的類的一個包裝。中的裝飾器模式中有一個的提案,使用一個以開頭的函數對中的及其屬性方法進行修飾。 1 什么是裝飾器模式 showImg(https://segmentfault.com/img/remote/1460000015970102?w=1127&h=563); 向一個現有的對...
摘要:包裝模式是這樣干的首先我們弄一個裝飾器,它實現了接口,以組合的方式接收我們的默認實現類。其實裝飾器抽象類的作用就是代理核心的功能還是由最簡單的實現類來做,只不過在擴展的時候可以添加一些沒有的功能而已。 前言 只有光頭才能變強 回顧前面: 給女朋友講解什么是代理模式 前一篇已經講解了代理模式了,今天要講解的就是裝飾模式啦~ 在看到FilterInputStream和FilterOutpu...
摘要:所以這是一篇插隊的文章,用于去理解中的裝飾器和概念。因此,該的作用就是根據入參返回具體的描述符。其次局部來看,裝飾器具體應用表達式是,其函數簽名和是一模一樣。等裝飾器語法,是和直接使用是等效等價的。 ================前言=================== 初衷:以系列故事的方式展現 MobX 源碼邏輯,盡可能以易懂的方式講解源碼; 本系列文章: 《【用故事解...
閱讀 2704·2021-09-26 10:19
閱讀 2146·2021-09-24 10:27
閱讀 2529·2021-09-01 10:42
閱讀 2311·2019-08-29 16:09
閱讀 2492·2019-08-29 15:17
閱讀 1455·2019-08-29 15:09
閱讀 642·2019-08-29 11:14
閱讀 2309·2019-08-26 13:25