摘要:在學(xué)習(xí)裝飾器語法之前,需要先溫習(xí)一下的一些基礎(chǔ)知識。函數(shù)最后必須返回。使用時也很簡單,如下在方法前面加上,就是裝飾器語法。裝備了,攻擊更強(qiáng)了。職業(yè)的基本攻擊穿上了,移動速度更快了。
在學(xué)習(xí)ES7裝飾器語法之前,需要先溫習(xí)一下ES5的一些基礎(chǔ)知識。
假設(shè)有對象如下:(便于理解)
var person = { name: "TOM" }
在ES5中,對象中的每個屬性都有一個特性值來描述這個屬性的特點,他們分別是:
configurable: 屬性是否能被delete刪除,當(dāng)值為false時,其他特性值也不能被改變,默認(rèn)值為true
enumerable: 屬性是否能被枚舉,也就是是否能被for in循環(huán)遍歷。默認(rèn)為true
writable: 是否能修改屬性值。默認(rèn)為true
value:具體的屬性值是多少,默認(rèn)為undefined
get:當(dāng)我們通過person.name訪問name的屬性值時,get將被調(diào)用。該方法可以自定義返回的具體值是多少。get默認(rèn)值為undefined
set:當(dāng)我們通過person.name = "Jake"設(shè)置name屬性值時,set方法將被調(diào)用,該方法可以自定義設(shè)置值的具體方式,set默認(rèn)值為undefined
需要注意的是,不能同時設(shè)置value,writeable與get set。
我們可以通過Object.defineProperty(操作單個)與Object.defineProperties(操作多個)來修改這些特性值。
// 三個參數(shù)分別為 target, key, descriptor(特性值的描述對象) Object.defineProperty(person, "name", { value: "TOM" }) // 新增 Object.defineProperty(person, "age", { value: 20 })
裝飾器語法與此類似,當(dāng)我們想要自定義一個裝飾器時,可以這樣寫:
function nameDecorator(target, key, descriptor) { descriptor.value = () => { return "jake"; } return descriptor; }
函數(shù)nameDecorator的定義會重寫被他裝飾的屬性(getName)。方法的三個參數(shù)與Object.defineProperty一一對應(yīng),分別指當(dāng)前的對象Person,被作用的屬性getName,以及屬性特性值的描述對象descriptor。函數(shù)最后必須返回descriptor。
使用時也很簡單,如下:
class Person { constructor() { this.name = "jake" } @nameDecorator getName() { return this.name; } } let p1 = new Person(); console.log(p1.getName())
在getName方法前面加上@nameDecorator,就是裝飾器語法。
自定義函數(shù)nameDecorator的參數(shù)中,target,就是裝飾的對象Person,key就是被裝飾的具體方法getName。
不能使用裝飾器對構(gòu)造函數(shù)進(jìn)行更改,如果要修改構(gòu)造函數(shù),則可以通過如下的方式來完成
function initDecorator(target, key, descriptor) { const fn = descriptor.value; // 改變傳入的參數(shù)值 descriptor.value = (...args) => { args[0] = "TOM"; return fn.apply(target, args); } return descriptor; } class Person { constructor(name, age) { this.init(name, age) } @initDecorator init(name, age) { this.name = name; this.age = age; } getName() { return this.name; } getAge() { return this.age; } } console.log(new Person("alex", 20).getName()); // TOM
如何希望裝飾器傳入一個指定的參數(shù),可以如下做。
// 注意這里的差別 function initDecorator(name) { return function(target, key, descriptor) { const fn = descriptor.value; descriptor.value = (...args) => { args[0] = name; return fn.apply(target, args); } return descriptor; } } class Person { constructor(name, age) { this.init(name, age) } @initDecorator("xiaoming") init(name, age) { this.name = name; this.age = age; } getName() { return this.name; } getAge() { return this.age; } } console.log(new Person("alex", 20).getName()); // xiaoming
這里利用了閉包的原理,將裝飾器函數(shù)外包裹一層函數(shù),以閉包的形式緩存了傳入的參數(shù)。
我們也可以對整個class添加裝飾器
function personDecorator(target) { // 修改方法 target.prototype.getName = () => { return "hahahahaha" } // 新增方法,因為內(nèi)部使用了this,因此一定不能使用箭頭函數(shù) target.prototype.getAge = function() { return this.age } return target; } @personDecorator class Person { constructor(name, age) { this.init(name, age) } init(name, age) { this.name = name; this.age = age; } getName() { return this.name; } } var p = new Person("alex", 30); console.log(p.getName(), p.getAge()); // hahahahaha 30
也可以傳參數(shù)
var xiaom = { name: "xiaom", age: 22 } function stuDecorator(person) { return function(target) { // 修改方法 target.prototype.getAge = () => { return person.age; } // 添加方法 target.prototype.getOther = () => { return "other info." } return target; } } function initDecorator(person) { return function(target, key, descriptor) { var method = descriptor.value; descriptor.value = () => { var ret = method.call(target, person.name); return ret; } } } @stuDecorator(xiaom) class Student { constructor(name, age) { this.init(name, age); } @initDecorator(xiaom) init(name, age) { this.name = name; this.age = age; } getAge() { return this.age; } getName() { return this.name; } } var p = new Student("hu", 18); console.log(p.getAge(), p.getName(), p.getOther()); // 22 "xiaom" "other info."
那么用ES7 的decorator來實現(xiàn)最開始的需求,則可以這樣做
import { cloth, weapon, shoes, defaultRole } from "./config"; // 基礎(chǔ)角色 class Role { constructor(role) { this.hp = role.hp; this.atk = role.atk; this.speed = role.speed; this.cloth = role.cloth; this.weapon = role.weapon; this.shoes = role.shoes; } run() {} attack() {} } function ClothDecorator(target) { target.prototype.getCloth = function(cloth) { this.hp += cloth.hp; this.cloth = cloth.name; } } function WeaponDecorator(target) { target.prototype.getWeapon = function(weapon) { this.atk += weapon.attack; this.weapon = weapon.name; } target.prototype.attack = function() { if (this.weapon) { console.log(`裝備了${this.weapon},攻擊更強(qiáng)了`); } else { console.log("戰(zhàn)士的基礎(chǔ)攻擊"); } } } function ShoesDecorator(target) { target.prototype.getShoes = function(shoes) { this.speed += shoes.speed; this.shoes = shoes.name; } target.prototype.run = function() { if (this.shoes) { console.log(`穿上了${this.shoes},移動速度更快了`); } else { console.log("戰(zhàn)士的奔跑動作"); } } } @ClothDecorator @WeaponDecorator @ShoesDecorator class Soldier extends Role { constructor(role) { const o = Object.assign({}, defaultRole, role); super(o); this.nickname = role.nickname; this.gender = role.gender; this.career = "戰(zhàn)士"; if (role.hp == defaultRole.hp) { this.hp = defaultRole.hp + 20; } if (role.speed == defaultRole.speed) { this.speed = defaultRole.speed + 5; } } run() { console.log("戰(zhàn)士的奔跑動作"); } attack() { console.log("戰(zhàn)士的基礎(chǔ)攻擊"); } } const base = { ...defaultRole, nickname: "alex", gender: "man" } const s = new Soldier(base); s.getCloth(cloth); console.log(s); s.getWeapon(weapon); s.attack(); console.log(s); s.getShoes(shoes); s.run(); console.log(s);
這里需要注意的是,裝飾者模式與直接使用瀏覽器支持的語法在實現(xiàn)上的一些區(qū)別。
ES7 Decorator重點在于對裝飾器的封裝,因此我們可以將上栗中的裝飾器多帶帶封裝為一個模塊。在細(xì)節(jié)上做了一些調(diào)整,讓我們封裝的裝飾器模塊不僅僅可以在創(chuàng)建戰(zhàn)士對象的時候使用,在我們創(chuàng)建其他職業(yè)例如法師,射手的時候也能夠正常使用。
export function ClothDecorator(target) { target.prototype.getCloth = function(cloth) { this.hp += cloth.hp; this.cloth = cloth.name; } } export function WeaponDecorator(target) { target.prototype.getWeapon = function(weapon) { this.atk += weapon.attack; this.weapon = weapon.name; } target.prototype.attack = function() { if (this.weapon) { console.log(`${this.nickname}裝備了${this.weapon},攻擊更強(qiáng)了。職業(yè):${this.career}`); } else { console.log(`${this.career}的基本攻擊`); } } } export function ShoesDecorator(target) { target.prototype.getShoes = function(shoes) { this.speed += shoes.speed; this.shoes = shoes.name; } target.prototype.run = function() { if (this.shoes) { console.log(`${this.nickname}穿上了${this.shoes},移動速度更快了。職業(yè):${this.career}`); } else { console.log(`${this.career}的奔跑動作`); } } }
可以利用該例子,感受Decorator與繼承的不同。
整理之后,Soldier的封裝代碼將會變得非常簡單
import { cloth, weapon, shoes, defaultRole } from "./config"; import { ClothDecorator, WeaponDecorator, ShoesDecorator } from "./equip"; import Role from "./Role"; @ClothDecorator @WeaponDecorator @ShoesDecorator class Soldier extends Role { constructor(roleInfo) { const o = Object.assign({}, defaultRoleInfo, roleInfo); super(o); this.nickname = roleInfo.nickname; this.gender = roleInfo.gender; this.career = "戰(zhàn)士"; if (roleInfo.hp == defaultRoleInfo.hp) { this.hp = defaultRoleInfo.hp + 20; } if (roleInfo.speed == defaultRoleInfo.speed) { this.speed = defaultRoleInfo.speed + 5; } } run() { console.log("戰(zhàn)士的奔跑動作"); } attack() { console.log("戰(zhàn)士的基礎(chǔ)攻擊"); } }
那么繼續(xù)上一篇文章的思考題,利用裝飾器可以怎么做呢?
補(bǔ)充:如何在構(gòu)建環(huán)境中支持ES7 Decorator語法
https://technologyadvice.gith...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/107064.html
摘要:所以這是一篇插隊的文章,用于去理解中的裝飾器和概念。因此,該的作用就是根據(jù)入?yún)⒎祷鼐唧w的描述符。其次局部來看,裝飾器具體應(yīng)用表達(dá)式是,其函數(shù)簽名和是一模一樣。等裝飾器語法,是和直接使用是等效等價的。 ================前言=================== 初衷:以系列故事的方式展現(xiàn) MobX 源碼邏輯,盡可能以易懂的方式講解源碼; 本系列文章: 《【用故事解...
摘要:本文介紹了,我們團(tuán)隊寫組件的最佳實踐。這樣可以避免類似之類的錯誤避免使用函數(shù)表達(dá)式的方式來定義組件,如下這看起來非常酷,但是在這里,通過函數(shù)表達(dá)式定義的函數(shù)卻是匿名函數(shù)。匿名函數(shù)也可能會導(dǎo)致測試庫出問題。 本文為譯文,已獲得原作者允許,原文地址:http://scottdomes.com/blog/ou... 當(dāng)我第一次開始寫 React 時,我發(fā)現(xiàn)多少個 React 教程,就有多少種...
摘要:安裝等相關(guān)依賴。通過啟動項目,進(jìn)行后續(xù)操作。自定義執(zhí)行狀態(tài)的改變。任何不在使用狀態(tài)的計算值將不會更新,直到需要它進(jìn)行副作用操作時。強(qiáng)烈建議始終拋出錯誤,以便保留原始堆棧跟蹤。 2018-08-14 learning about work begin:2018-08-13 step 1 熟悉react 寫法 step 2 mobx 了解&使用 step 3 thrift接口調(diào)用過程 Re...
摘要:前言原本說接下來會專注學(xué)但是最新工作又學(xué)習(xí)了一些有意思的庫於是就再寫下來做個簡單的入門之前我寫過一篇文章這個也算是作為一個補(bǔ)充吧這次無非就是類似筆記把認(rèn)為的一些關(guān)鍵點記下來有些地方還沒用到就衹是描述一下代碼有些自己寫的有些文檔寫的很好就搬下 前言 原本說接下來會專注學(xué)nodejs,但是最新工作又學(xué)習(xí)了一些有意思的庫,於是就再寫下來做個簡單的入門,之前我寫過一篇文章,這個也算是作為一個補(bǔ)...
摘要:前言初衷以系列故事的方式展現(xiàn)源碼邏輯,盡可能以易懂的方式講解源碼本系列文章用故事解讀源碼一用故事解讀源碼二用故事解讀源碼三用故事解讀源碼四裝飾器和用故事解讀源碼五文章編排每篇文章分成兩大段,第一大段以簡單的偵探系列故事的形式講解所涉及人物場 ================前言=================== 初衷:以系列故事的方式展現(xiàn) MobX 源碼邏輯,盡可能以易懂的方式...
閱讀 1892·2021-09-24 09:48
閱讀 3236·2021-08-26 14:14
閱讀 1692·2021-08-20 09:36
閱讀 1480·2019-08-30 15:55
閱讀 3642·2019-08-26 17:15
閱讀 1438·2019-08-26 12:09
閱讀 618·2019-08-26 11:59
閱讀 3336·2019-08-26 11:57