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