Junwen's home
  • ES6

    • ES6 Decorator
    • ES6核心特性
    • Promise&Generator
  • js原理

    • 简单实现bind、apply和call
    • 如何遍历一个dom tree
    • 实现函数currying
    • 实现一个event
    • 详解js的继承
    • 详解requestAnimationFrame
    • Canvas api详解
    • DOM事件
    • EventLoop详解
    • JavaScript的内存管理
    • JavaScript的运行机制
    • Math对象
    • new操作符都做了什么
    • create基本实现原理
    • Set、Map、WeakSet和WeakMap
    • web worker原理
    • WebGL教程(MDN)
  • jsInfoSeries

    • 简介
    • JavaScript基础知识
    • 基础知识2
    • 基础知识3
    • 基础知识4
  • 技巧

    • 5个js解构有趣用途
    • 如何使用set提高代码性能
    • cordova构建项目时的问题
    • js中轻松遍历对象属性的几种方式
  • 怎么写出更好的css
  • BFC详解
  • box-shadow详解
  • CSS小技巧
  • Grid布局详解
HTML
  • IP十问
  • http笔试
  • http协议
  • 浏览器原理
  • 浏览器缓存其实就这么一回事儿
  • 浏览器兼容性问题
  • 移动端开发兼容性适配
  • 前端性能优化
  • 前端如何进行seo优化
  • webpack

    • webpack HMR
    • webpack优化基本方法
  • leetcode题解

    • 两数之和
    • 判断整数是否为回文串
    • 无重复字符的最长子串
  • Js链表
  • JavaScript排序
  • React

    • 虚拟DOM原理理解
    • React Hook
    • 组件复用指南
  • Vue

    • Vue举一反三
面试题
读书笔记
GitHub (opens new window)

Syun0216

多读书多种树
  • ES6

    • ES6 Decorator
    • ES6核心特性
    • Promise&Generator
  • js原理

    • 简单实现bind、apply和call
    • 如何遍历一个dom tree
    • 实现函数currying
    • 实现一个event
    • 详解js的继承
    • 详解requestAnimationFrame
    • Canvas api详解
    • DOM事件
    • EventLoop详解
    • JavaScript的内存管理
    • JavaScript的运行机制
    • Math对象
    • new操作符都做了什么
    • create基本实现原理
    • Set、Map、WeakSet和WeakMap
    • web worker原理
    • WebGL教程(MDN)
  • jsInfoSeries

    • 简介
    • JavaScript基础知识
    • 基础知识2
    • 基础知识3
    • 基础知识4
  • 技巧

    • 5个js解构有趣用途
    • 如何使用set提高代码性能
    • cordova构建项目时的问题
    • js中轻松遍历对象属性的几种方式
  • 怎么写出更好的css
  • BFC详解
  • box-shadow详解
  • CSS小技巧
  • Grid布局详解
HTML
  • IP十问
  • http笔试
  • http协议
  • 浏览器原理
  • 浏览器缓存其实就这么一回事儿
  • 浏览器兼容性问题
  • 移动端开发兼容性适配
  • 前端性能优化
  • 前端如何进行seo优化
  • webpack

    • webpack HMR
    • webpack优化基本方法
  • leetcode题解

    • 两数之和
    • 判断整数是否为回文串
    • 无重复字符的最长子串
  • Js链表
  • JavaScript排序
  • React

    • 虚拟DOM原理理解
    • React Hook
    • 组件复用指南
  • Vue

    • Vue举一反三
面试题
读书笔记
GitHub (opens new window)
  • ES6

    • ES6 Decorator
      • ES6核心特性
      • Promise&Generator
    • js原理

      • 简单实现bind、apply和call
      • 如何遍历一个dom tree
      • 实现函数currying
      • 实现一个event
      • 详解js的继承
      • 详解requestAnimationFrame
      • Canvas api详解
      • DOM事件
      • EventLoop详解
      • JavaScript的内存管理
      • JavaScript的运行机制
      • Math对象
      • new操作符都做了什么
      • Object.create基本实现原理
      • Set、Map、WeakSet和WeakMap
      • web worker原理
      • WebGL教程(MDN)
    • jsInfoSeries

      • 简介
      • JavaScript基础知识
      • 基础知识2
      • 基础知识3
      • 基础知识4
    • 技巧

      • 5个js解构有趣用途
      • 如何使用set提高代码性能
      • cordova构建项目时的问题
      • js中轻松遍历对象属性的几种方式
    • JavaScript
    junwen
    2019-08-06

    ES6 Decorator

    探寻ECMAScript的装饰器Decorator

    参考探寻ECMAScript的装饰器Decorator (opens new window)

    装饰器可以说是解决了不同类之间共享方法的问题(可以看做是弥补继承的不足)
    在未来JS也将引入这个概念,并且babel对他有很好的支持。

    # 使用babel

    安装babel:

    npm i babel-cli babel-preset-env -D // babel
    npm i babel-plugin-transform-decorators-legact -D // babel 装饰器
    
    1
    2

    在.babelrc中写入:

    {
      "presets": ["env"],
      "plugins": ["transform-decorators-legacy"]
    }
    
    1
    2
    3
    4

    # 装饰器

    function decorateArmour(target, key, descriptor) {
      const method = descriptor.value;
      let moreDef = 100;
      let ret;
      descriptor.value = (...args)=>{
        args[0] += moreDef;
        ret = method.apply(target, args);
        return ret;
      }
      return descriptor;
    }
    
    class Man{
      constructor(def = 2,atk = 3,hp = 3){
        this.init(def,atk,hp);
      }
    
      @decorateArmour
      init(def,atk,hp){
        this.def = def; // 防御值
        this.atk = atk;  // 攻击力
        this.hp = hp;  // 血量
      }
      toString(){
        return `防御力:${this.def},攻击力:${this.atk},血量:${this.hp}`;
      }
    }
    
    var tony = new Man();
    
    console.log(`当前状态 ===> ${tony}`);
    // 输出:当前状态 ===> 防御力:102,攻击力:3,血量:3
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32

    装饰器接受三个参数,这三个参数和Object.defineProperty()基本一致,分别为:

    1. 需要定义属性的对象 -- 被装饰的类
    2. 需定义修改的属性的名字 -- 被装饰的属性名
    3. 将被定义或修改的属性的描述符 -- 属性的描述对象

    再看上面的代码:

    1. target是 Man {} 这个类
    2. key 是被装饰的函数 init()
    3. descriptor 和 Object.defineProperty() 一样: {value: [Function], writable: true, enumerable: false, configurable: true}

    descriptor.value = (...args)=> 中的 args 是一个数组,分别对应 def、atk、hp,给 def + 100,然后再执行 method(即被装饰的函数),最后返回 descriptor。

    # 带参数的装饰类方法

    function decorateArmour(num) {
      return function(target, key, descriptor) {
        const method = descriptor.value;
        let moreDef = num || 100;
        let ret;
        descriptor.value = (...args)=>{
          args[0] += moreDef;
          ret = method.apply(target, args);
          return ret;
        }
        return descriptor;
      }
    }
    
    class Man{
      constructor(def = 2,atk = 3,hp = 3){
        this.init(def,atk,hp);
      }
    
      @decorateArmour(20)
      init(def,atk,hp){
        this.def = def; // 防御值
        this.atk = atk;  // 攻击力
        this.hp = hp;  // 血量
      }
      toString(){
        return `防御力:${this.def},攻击力:${this.atk},血量:${this.hp}`;
      }
    }
    
    var tony = new Man();
    console.log(`当前状态 ===> ${tony}`);
    // 输出:当前状态 ===> 防御力:22,攻击力:3,血量:3
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33

    # 装饰 类

    上面两个装饰器都是对类里面的函数进行装饰,改变了类的静态属性;除此之外,还可以对类进行装饰,给类添加方法或者修改方法(通过被装饰类的 prototype):

    function decorateArmour(num) {
      return function(target, key, descriptor) {
        const method = descriptor.value;
        let moreDef = num || 100;
        let ret;
        descriptor.value = (...args)=>{
          args[0] += moreDef;
          ret = method.apply(target, args);
          return ret;
        }
        return descriptor;
      }
    }
    
    
    function addFunc(target) {
      target.prototype.addFunc = () => {
      	return 'i am addFunc'
      }
      return target;
    }
    
    @addFunc
    class Man{
      constructor(def = 2,atk = 3,hp = 3){
        this.init(def,atk,hp);
      }
    
      @decorateArmour(20)
      init(def,atk,hp){
        this.def = def; // 防御值
        this.atk = atk;  // 攻击力
        this.hp = hp;  // 血量
      }
      toString(){
        return `防御力:${this.def},攻击力:${this.atk},血量:${this.hp}`;
      }
    }
    
    var tony = new Man();
    console.log(`当前状态 ===> ${tony}`)
    console.log(tony.addFunc());
    
    // 输出:当前状态 ===> 防御力:22,攻击力:3,血量:3
    // 输出:i am addFunc
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45

    # 装饰 普通函数

    不建议装饰,因为变量提升会产生系列问题

    # 参考文章

    • Exploring EcmaScript Decorators (opens new window)
    • javascript-decorators (opens new window)
    • ES7 Decorator 装饰者模式 (opens new window)
    在Github上编辑此页 (opens new window)
    #ES6#JavaScript
    上次更新: 3/22/2021, 3:47:15 AM
    ES6核心特性

    ES6核心特性→

    最近更新
    01
    如何打造全链路项目生命周期的统一交付平台
    04-10
    02
    如何建立前端标准化研发流程
    04-10
    03
    如何从0到1一步步成体系地搭建CI
    04-10
    更多文章>
    Theme by Vdoing | Copyright © 2019-2021 Syun
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式