在学习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,writeableget 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'}@nameDecoratorgetName() {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)}@initDecoratorinit(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...

MobX详解(二):ES7 装饰器 decorator相关推荐

  1. 详解Python的装饰器

    详解Python的装饰器 Python中的装饰器是你进入Python大门的一道坎,不管你跨不跨过去它都在那里. 为什么需要装饰器 我们假设你的程序实现了say_hello()和say_goodbye( ...

  2. 详解设计模式:装饰器模式

    装饰器模式(Decorator Pattern)也称为包装模式(Wrapper Pattern),是 GoF 的 23 种设计模式中的一种结构型设计模式.装饰器模式 是指在不改变原有对象的基础之上,将 ...

  3. python装饰器setter_第7.27节 Python案例详解: @property装饰器定义属性访问方法getter、setter、deleter...

    上节详细介绍了利用@property装饰器定义属性的语法,本节通过具体案例来进一步说明. 一.    案例说明 本节的案例是定义Rectangle(长方形)类,为了说明问题,除构造函数外,其他方法都只 ...

  4. 《是碰巧还是执着?python所阅读的每一场知识点,唯一的共同点就是——参赛选手中,有详解Python的装饰器!》

    Python中的装饰器是你进入Python大门的一道坎,不管你跨不跨过去它都在那里. 为什么需要装饰器 我们假设你的程序实现了say_hello()和say_goodbye()两个函数. def sa ...

  5. python装饰器详解-Python 函数装饰器

    讲 Python 装饰器前,我想先举个例子,虽有点污,但跟装饰器这个话题很贴切. 每个人都有的内裤主要功能是用来遮羞,但是到了冬天它没法为我们防风御寒,咋办?我们想到的一个办法就是把内裤改造一下,让它 ...

  6. python类装饰器详解-Python类装饰器实现方法详解

    本文实例讲述了Python类装饰器.分享给大家供大家参考,具体如下: 编写类装饰器 类装饰器类似于函数装饰器的概念,但它应用于类,它们可以用于管理类自身,或者用来拦截实例创建调用以管理实例. 单体类 ...

  7. python类装饰器详解-Python 类装饰器解析

    1. 类装饰器(都不带参数) class ClassDeco: def __init__(self, func): self.func = func def __call__(self, *args, ...

  8. python高级语法装饰器_Python高级编程——装饰器Decorator超详细讲解上

    Python高级编程--装饰器Decorator超详细讲解(上篇) 送你小心心记得关注我哦!! 进入正文 全文摘要 装饰器decorator,是python语言的重要特性,我们平时都会遇到,无论是面向 ...

  9. 装饰器Decorator(函数的装饰)

    一.LEGB函数作用域的优先级和解析 函数是function的一个对象,被调用完后内部变量就会被回收,被引用的除外(例如return的变量) 1.   local :函数内部作用域 2.  enclo ...

最新文章

  1. 修改 IntelliJ IDEA 默认配置路径
  2. 移动文件流的读写指针---fseek
  3. 成功解决sys:1: DtypeWarning: Columns (39,41,42,217) have mixed types.Specify dtype option on import or s
  4. 光盘引导和网络安装linux系统
  5. php使用Header函数,PHP_AUTH_PW和PHP_AUTH_USER做用户验证及缺点
  6. netbeans java中文_Ubuntu 下jdk安装中文字体 java 解决netbeans 方块字 中文乱码
  7. 漫步数理统计十五——两个随机变量的分布
  8. android自动发送dtmf,Android发送dtmf键盘事件(模拟通话界面键盘事件)
  9. 失败程序员的十年总结
  10. 空降领导(CTO/技术总监)如何安全落地
  11. 计算机win7不断重启,win7系统无故自动重启的解决办法
  12. CameraRaw升级
  13. Linux-lsxxx
  14. 智能硬件的一些框架性内容
  15. 五环电阻和四环电阻的区别是什么
  16. 沙奎尔·奥尼尔——盘点那些“巨人”的事①
  17. uni-app视频加图片轮播
  18. 麦肯锡 7S 诊断模型
  19. 前端面试题目大全(附答案)
  20. 怎么把paper快快读了

热门文章

  1. 7步搞定Python数据可视化,业界大牛出品教程,Jupyter、Colab都有在线版
  2. vue cli3.0 引入eslint 结合vscode使用
  3. 使用react全家桶制作博客后台管理系统
  4. JVM(2)之 JAVA堆
  5. 从创业到成功,SaaS巨头Salesforce靠的是这七大秘诀
  6. varnish配置详解
  7. Java各种数据类型详细介绍及其区别
  8. Windows 根据进程名杀死进程 kill
  9. EVEREST Corporate Edition 使用SQL保存数据的简易方法
  10. Tungsten Fabric SDN — 与 Bare Metal 的集成架构