javascript 保存原函数_设计模式(11)[JS版]JavaScript中的注解之装饰器模式

目录 1 什么是装饰器模式? 2 装饰器模式的主要参与者有哪些 3 代码实现 4 实例应用 5 ES7 中的 decorator 6 总结

1 什么是装饰器模式?

装饰器模式模式动态地扩展了(装饰)一个对象的行为,同时又不改变其结构。在运行时添加新的行为的能力是由一个装饰器对象来完成的,它 "包裹 "了原始对象,用来提供额外的功能。多个装饰器可以添加或覆盖原始对象的功能。装饰器模式属于结构型模式。 和适配器模式不同的是,适配器模式是原有的对象不能用了,而装饰器模式是原来的对象还能用,在不改变原有对象结构和功能的前提下,为对象添加新功能。装饰器的一个例子是安全管理,其中业务对象被赋予了额外的访问权限,这取决于经过认证的用户的权限。例如,人力资源经理得到一个雇员对象,该对象已经附加(即装饰了)查看雇员的工资记录权限,这样该员工就可以查看工资信息了。装饰器通过允许运行时更改,为静态类型的语言提供灵活性。但是,JavaScript是一种动态语言,并且在运行时扩展对象的能力已融入该语言本身。

2 装饰器模式的主要参与者有哪些

85517514aa2ea15f2c9bd15a300df94a.png 参与该模式的对象有:

客户端(Client) :维护一个对被装饰的组件的引用。 组件(Component) :添加了附加功能的对象。       装饰器(Decorator) :
          1.通过保持对Component的引用来 "包装 "它。
          2. 定义了一个符合Component接口的接口。
          3.实现附加功能(图中addedMembers)。

3 代码实现

在下面的代码中,一个User对象被一个DecoratedUser对象装饰(增强),它扩展了User的地址属性。因为原始接口必须保持不变,所以user.name会被分配给this.name。另外,DecoratedUser的say方法隐藏了User的say方法。

这是装饰器模式的经典实现,但是JavaScript本身的一些语法,就可以更有效的在运行时扩展对象,所以在实际开发中我们一般不会用到这种方法。日志函数用来记录和显示结果。

<html>        <head>                <meta charset="utf-8">                <title>装饰器模式:公众号AlbertYangtitle>        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>
!DOCTYPE htmlhtmlheadmeta charset=\"utf-8\"title/title/headbody/bodyscript/script/html

4 实例应用

<html>  <head>    <meta charset="utf-8">    <meta http-equiv="X-UA-Compatible" content="IE=edge">    <title>装饰器模式:公众号AlbertYangtitle>  head>  <body>    <script>      //实例1 - 多重onload绑定      function addLoadEvent(fn) {        var oldEvent = window.onload;        if (typeof window.onload != 'function') {          window.onload = fn;        } else {          window.onload = function() {            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 = Date.now();          let result = func(...args);          const used = Date.now() - 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>
!DOCTYPE htmlhtmlheadmeta charset=\"utf-8\"meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"title/title/headbodyscript

5 ES7 中的 decorator

在ES7中提供了一种类似于java注解的语法糖来实现装饰器模式。decorator的实现依赖于ES5的Object.defineProperty方法来进行扩展和封装的。装饰器是一种函数,写法是 @+函数名。在使用它之前需要引入babel模块 transform-decorators-legacy 编译成 ES5 或 ES6。

@testableclass MyTestableClass {// ...}function testable(target) {   target.isTestable = true;}MyTestableClass.isTestable // true@decoratorclass A {}// 等同于class 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'

装饰方法,让某个方法只读,不能修改

function readonly(target, name, descriptor){    // descriptor属性描述对象 (Object.defineProperty 会用到)    // descriptor对象原来的值如下    // {    //   value: specifiedFunction,    //   enumerable: false,    //   configurable: true,    //   writable: true    // };    descriptor.writable = false;    return descriptor;  }    class Person {      constructor() {          this.first = 'A'          this.last = 'B'      }        @readonly      testname() { return `${this.first} ${this.last}` }  }    var p = new Person()  console.log(p.testname())  p.testname = function () {} // 这里会报错,因为 name 是只读属性

实现一个日志打印装饰器

function log(target, name, descriptor) {    var oldValue = descriptor.value;    // name 是修饰的方法名字    descriptor.value = function() {      console.log(`Calling ${name} with`, arguments);      return oldValue.apply(this, arguments);    };      return descriptor;  }    class Math {    @log    add(a, b) {      return a + b;    }  }    const math = new Math();  const result = math.add(2, 4);  console.log('result', result);

装饰类的时候,一般主要是看target(装饰对象)第一个参数。装饰方法的时候,一般主要看的是descriptor(描述)第三个参数。

        core-decorators.js是一个第三方模块,提供了几个常见的装饰器,通过它可以更好地理解装饰器。

官网文档:https://github.com/jayphelps/core-decorators.js

6 总结

装饰器模式是一种常见的结构型模式,在不改变类或对象本身结构的情况下,在程序的运行期间动态的为对象或类添加功能。与继承相比,装饰者模式是一种更轻便灵活的做法。Decorator 虽然原理非常简单,却可以实现很多实用又方便的功能,目前前端领域很多框架和库都在大规模使用这个特性,像 mobx中@observable、Angular中的大量应用都证明了其高可用性。个人觉得在一些开发框架中尝试加入装饰器可以提供更简洁以及高效的代码质量。

今天的学习就到这里,你可以使用今天学习的技巧来改善一下你曾经的代码,如果想继续提高,欢迎关注我,每天学习进步一点点,就是领先的开始。如果觉得本文对你有帮助的话,欢迎点赞,评论,转发!!!

2f6938a638e09bef2ff73a636997473d.png

利用 TensorFlow 训练自己的目标识别器。本文内容来自于我的毕业设计,基于 TensorFlow 1.15.0,其他 TensorFlow 本运行可能存在问题。.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计,皆可应用在项目、毕业设计、课程设计、期末/期/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值