MobX详解(二):ES7 装饰器 decorator
在学习ES7装饰器语法之前,需要先温习一下ES5的一些基础知识。
假设有对象如下:(便于理解)
var person = {name: 'TOM'
}
在ES5中,对象中的每个属性都有一个特性值来描述这个属性的特点,他们分别是:
configurable
: 属性是否能被delete删除,当值为false时,其他特性值也不能被改变,默认值为trueenumerable
: 属性是否能被枚举,也就是是否能被for in循环遍历。默认为truewritable
: 是否能修改属性值。默认为truevalue
:具体的属性值是多少,默认为undefinedget
:当我们通过person.name
访问name的属性值时,get将被调用。该方法可以自定义返回的具体值是多少。get默认值为undefinedset
:当我们通过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'}@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相关推荐
- 详解Python的装饰器
详解Python的装饰器 Python中的装饰器是你进入Python大门的一道坎,不管你跨不跨过去它都在那里. 为什么需要装饰器 我们假设你的程序实现了say_hello()和say_goodbye( ...
- 详解设计模式:装饰器模式
装饰器模式(Decorator Pattern)也称为包装模式(Wrapper Pattern),是 GoF 的 23 种设计模式中的一种结构型设计模式.装饰器模式 是指在不改变原有对象的基础之上,将 ...
- python装饰器setter_第7.27节 Python案例详解: @property装饰器定义属性访问方法getter、setter、deleter...
上节详细介绍了利用@property装饰器定义属性的语法,本节通过具体案例来进一步说明. 一. 案例说明 本节的案例是定义Rectangle(长方形)类,为了说明问题,除构造函数外,其他方法都只 ...
- 《是碰巧还是执着?python所阅读的每一场知识点,唯一的共同点就是——参赛选手中,有详解Python的装饰器!》
Python中的装饰器是你进入Python大门的一道坎,不管你跨不跨过去它都在那里. 为什么需要装饰器 我们假设你的程序实现了say_hello()和say_goodbye()两个函数. def sa ...
- python装饰器详解-Python 函数装饰器
讲 Python 装饰器前,我想先举个例子,虽有点污,但跟装饰器这个话题很贴切. 每个人都有的内裤主要功能是用来遮羞,但是到了冬天它没法为我们防风御寒,咋办?我们想到的一个办法就是把内裤改造一下,让它 ...
- python类装饰器详解-Python类装饰器实现方法详解
本文实例讲述了Python类装饰器.分享给大家供大家参考,具体如下: 编写类装饰器 类装饰器类似于函数装饰器的概念,但它应用于类,它们可以用于管理类自身,或者用来拦截实例创建调用以管理实例. 单体类 ...
- python类装饰器详解-Python 类装饰器解析
1. 类装饰器(都不带参数) class ClassDeco: def __init__(self, func): self.func = func def __call__(self, *args, ...
- python高级语法装饰器_Python高级编程——装饰器Decorator超详细讲解上
Python高级编程--装饰器Decorator超详细讲解(上篇) 送你小心心记得关注我哦!! 进入正文 全文摘要 装饰器decorator,是python语言的重要特性,我们平时都会遇到,无论是面向 ...
- 装饰器Decorator(函数的装饰)
一.LEGB函数作用域的优先级和解析 函数是function的一个对象,被调用完后内部变量就会被回收,被引用的除外(例如return的变量) 1. local :函数内部作用域 2. enclo ...
最新文章
- 修改 IntelliJ IDEA 默认配置路径
- 移动文件流的读写指针---fseek
- 成功解决sys:1: DtypeWarning: Columns (39,41,42,217) have mixed types.Specify dtype option on import or s
- 光盘引导和网络安装linux系统
- php使用Header函数,PHP_AUTH_PW和PHP_AUTH_USER做用户验证及缺点
- netbeans java中文_Ubuntu 下jdk安装中文字体 java 解决netbeans 方块字 中文乱码
- 漫步数理统计十五——两个随机变量的分布
- android自动发送dtmf,Android发送dtmf键盘事件(模拟通话界面键盘事件)
- 失败程序员的十年总结
- 空降领导(CTO/技术总监)如何安全落地
- 计算机win7不断重启,win7系统无故自动重启的解决办法
- CameraRaw升级
- Linux-lsxxx
- 智能硬件的一些框架性内容
- 五环电阻和四环电阻的区别是什么
- 沙奎尔·奥尼尔——盘点那些“巨人”的事①
- uni-app视频加图片轮播
- 麦肯锡 7S 诊断模型
- 前端面试题目大全(附答案)
- 怎么把paper快快读了