设计模式(11)[JS版]-JavaScript中的注解之装饰器模式
⽬录
1 什么是装饰器模式?
装饰器模式模式动态地扩展了(装饰)⼀个对象的⾏为,同时⼜不改变其结构。在运⾏时添加新的⾏为的能⼒是由⼀个装饰器对象来完成的,它 "包裹 "了原始对象,⽤来提供额外的功能。多个装饰器可以添加或覆盖原始对象的功能。装饰器模式属于结构型模式。和适配器模式不同的是,适配器模式是原有的对象不能⽤了,⽽装饰器模式是原来的对象还能⽤,在不改变原有对象结构和功能的前提下,为对象添加新功能。
装饰器的⼀个例⼦是安全管理,其中业务对象被赋予了额外的访问权限,这取决于经过认证的⽤户的权限。例如,⼈⼒资源经理得到⼀个雇员对象,该对象已经附加(即装饰了)查看雇员的⼯资记录权限,这样该员⼯就可以查看⼯资信息了。装饰器通过允许运⾏时更改,为静态类型的语⾔提供灵活性。但是,JavaScript是⼀种动态语⾔,并且在运⾏时扩展对象的能⼒已融⼊该语⾔本⾝。
2 装饰器模式的主要参与者有哪些
参与该模式的对象有:
客户端(Client) :维护⼀个对被装饰的组件的引⽤。
组件(Component) :添加了附加功能的对象。
装饰器(Decorator) :
1.通过保持对Component的引⽤来 "包装 "它。
2. 定义了⼀个符合Component接⼝的接⼝。
3.实现附加功能(图中addedMembers)。
3 代码实现
在下⾯的代码中,⼀个User对象被⼀个DecoratedUser对象装饰(增强),它扩展了User的地址属性。因为原始接⼝必须保持不变,所以user.name会被分配给this.name。另外,DecoratedUser的say⽅法隐藏了User的say⽅法。
这是装饰器模式的经典实现,但是JavaScript本⾝的⼀些语法,就可以更有效的在运⾏时扩展对象,所以在实际开发中我们⼀般不会⽤到这种⽅法。⽇志函数⽤来记录和显⽰结果。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>装饰器模式:AlbertYang</title>
</head>
<body>
</body>
<script>
var User = function(name) {
this.name = name;
this.say = function() {
log.add("我是" + this.name);
};
}
var DecoratedUser = function(user, city, street) {
this.user = user;
this.name = user.name; // 确保接⼝保持不变
this.city = city;
this.street = street;
this.say = function() {
log.add("我是" + this.name + ", 住在" + this.city + ", " +
this.street);
};
}
// ⽇志函数
var log = (function() {
var log = "";
return {
add: function(msg) {
log += msg + "\n";
},
show: function() {
console.info("%c%s", "color:red; font-size:18px", log); log = "";
}
}
})();
function run() {
var user = new User("张三");
user.say();
var decorated = new DecoratedUser(user, "上海", "宝⼭路街道"); decorated.say();
log.show();
}
run();
</script>
</html>
4 实例应⽤
<!DOCTYPE html>
<html>
<head>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>装饰器模式:AlbertYang</title>
</head>
<body>
<script>
//实例1 - 多重onload绑定
function addLoadEvent(fn) {
var oldEvent = load;
if (load != 'function') {
} else {
oldEvent();
fn();
};
}
}
function fn1() {
console.log('加载函数1');
}
function fn2() {
console.log('加载函数2');
}
function fn3() {
console.log('加载函数3');
}
addLoadEvent(fn1);
addLoadEvent(fn2);
addLoadEvent(fn3);
//实例2 - 前置/后置处理函数(AOP⾯向切⾯编程)
Function.prototype.before = function(beforfunc) {
var self = this;//⽤来保存调⽤这个函数的引⽤,如myFunc调⽤此函数,则self指向myFunc
var outerArgs = Array.prototype.slice.call(arguments, 1);
return function() {//返回⼀个函数,相当于⼀个代理函数,也就是说,这⾥包含了原函数和新函数,原函数指的是myFunc,新函数指的是beforfunc var innerArgs = Array.prototype.slice.call(arguments);
beforfunc.apply(this, innerArgs);//修正this的指向,将this指针指向beforfunc,将myFunc接收的参数传
给beforfunc处理。
self.apply(this, outerArgs);//执⾏原函数
};
};
Function.prototype.after = function(afterfunc) {
var self = this;
var outerArgs = Array.prototype.slice.call(arguments, 1);
return function() {
var innerArgs = Array.prototype.slice.call(arguments);
self.apply(this, outerArgs);
afterfunc.apply(this, innerArgs);
};
};
var func = function(name) {
console.log('我是' + name);
};
var beforefunc = function(age) {
console.log('我' + age + '岁了');
};
};
var afterfunc = function(gender) {
console.log('我是⼀个' + gender);
};
var beforeFunc = func.before(beforefunc, '张三');
var afterFunc = func.after(afterfunc, '张三');
beforeFunc('12');
afterFunc('男⼈');
//实例3 - 计算函数执⾏⽤时
function log(func) {
return function(...args) {
const start = w();
let result = func(...args);
const used = w() - start;
console.log(`调⽤${func.name} (${args})函数⽤了 ${used} 毫秒。`);
return result;
};
}
function calculate(times) {
let sum = 0;
let i = 1;
while (i < times) {
sum += i;
i++;
}
return sum;
}
runCalculate = log(calculate);
let result = runCalculate(100000);
console.log(result);
</script>
</body>
</html>
5 ES7 中的 decorator
在ES7中提供了⼀种类似于java注解的语法糖来实现装饰器模式。decorator的实现依赖于ES5的Object.defineProperty⽅法来进⾏扩展和封装的。装饰器是⼀种函数,写法是 @+函数名。在使⽤它之前需要引⼊babel模块 transform-decorators-legacy 编译成 ES5 或ES6。
@testable
class MyTestableClass {
// ...
}
function testable(target) {
target.isTestable = true;
}
MyTestableClass.isTestable // true
@decorator
class A {}
// 等同于
js argumentsclass A {}
A = decorator(A) || A;
上⾯代码中,@testable就是⼀个装饰器。它修改了MyTestableClass这个类的属性,为它加上了静态属性isTestable。testable函数的参数target是MyTestableClass类本⾝。装饰器是⼀个对类进⾏处理的函数。装饰器函数的第⼀个参数,就是所要装饰的⽬标类。
我们来做⼀个常⽤的mixins混合装饰器,来把⼀个类⾥⾯属性和⽅法全部添加到另⼀个类上
function mixins(...list) {
return function (target) {
Object.assign(target.prototype, ...list)
}
}
const Foo = {
foo() { alert('foo') }
}
@mixins(Foo)
class MyClass {}
let obj = new MyClass();
obj.foo() // 'foo'
装饰⽅法,让某个⽅法只读,不能修改
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论