您现在的位置:首页 >> 前端 >> 内容

ES7decorator总结

时间:2017/9/9 9:10:00 点击:

  核心提示:平时我们用decorator来封装一些和原有类或者react组件(高阶组件)本身无关的功能。比如说埋点,路由,以及扩展的功能等,非常好用,在此简单记录下。decorator可以装饰对象和属性,下面就分...

平时我们用decorator来封装一些和原有类或者react组件(高阶组件)本身无关的功能。比如说埋点,路由,以及扩展的功能等,非常好用,在此简单记录下。

decorator可以装饰对象属性,下面就分开介绍。

准备工作

安装babel转码。

npm install --save-dev babel-cli babel-plugin-transform-decorators-legacy 

根目录配置.babelrc

{
  "presets": ["env"],
  "plugins": ["transform-decorators-legacy"]
}

package.json:

// npm init 后的package.json
{
    "name": "my-project",
    "version": "1.0.0",
    "scripts": {
     // 写入
     "build": "./node_modules/.bin/babel 你的原es6.js -o 转成的es5.js"
     // 目录写法
     // "build": "./node_modules/.bin/babel src -d lib"
    },
    "devDependencies": {
      "babel-cli": "^6.0.0"
    }
  }

babel-cli安装在全局的话,就不需要按照上面在以上script中添加build了。
运行:

npm run build // 全局babel-cli 使用 babel 你的原es6.js > 转成的es5.js

即可生成可运行的es5文件,然后使用node file.js在node里运行,或者引入html中即可。
当然也可以直接在 babel网站 中编辑转码。

decorator 装饰属性

装饰属性,会在属性注册到类prototype之前先执行装饰器,先看源码怎么写。

// 定义一个装饰器函数,里面的target这些参数和Object.defineProperty是对应的
function ready(target, name, descripter) {
  descripter.writable = false;  // 改写writable属性
  return descripter;            // 注意,返回属性描述对象descriptor
}
class A() {
  @ready
  b() {
    console.log('f');
  }
}

再看babel转义成es5的核心代码:

function applyDecorator(target, property, decorators, descriptor, context) {
  // 定义一个新的描述对象
  var desc = {};
  // 将target.property 的descriptor挂载在desc上
  // 从后往前依次覆盖前面的desc上的属性
  desc = decorators.slice().reverse().reduce(function(desc, decorator) {
    return decorator(target, propery, desc) || desc;
  });

  return desc;
}
applyDecorator(A.prototype, 'b', [ready], Object.getOwnPropertyDescriptor(A.prototype, 'b'), A.prototype);

经过ready处理后的descriptor 返回到类A(Class)的原型上的属性b上,之后对b方法的重写将会被禁止,这样我们可以针对b方法进行一些限制、拦截和改造性质的操作,包括get、 set方法。

另外,这里的reduce用的非常好。将含有处理函数的数组倒置,,然后使用reduce,每次处理函数返回值作为下一次的第一个属性再次传入,下一个处理函数继续处理返回值,redux库插件处理state就是这种思路。

decorator 装饰对象

装饰对象是大家使用decorator最广泛的场景。

function filter(flag) {
  return function(target) {
    // handler flag 
    // 也可以写一些自定义的方法挂载在target上
    target.getName = function() {
      return 'wf'
    };
  }
}

@filter(true)
class A {
  constructor() {
    this.name = "my"
  }
  getName() {
    return this.name;
  }
}

const a = new A();
console.log(a.getName()); // wf

最终转化成es5,直接传入 A,内部改写A的方法。

A = filter(true)(A);

也就是说,装饰器通过传入对象或者对象及方法名,通过改写他的行为。
改写可以通过Object.defineProperty,也可以通过target.otherName的方式,定义和赋值的区别,defineProperty这种能设置属性的特性,限制扩展等。而直接赋值会受到访问器属性的get``set的影响,也会影响访问器属性。

这样我们就能在上面能访问内部的属性,同时也可以添加许多扩展性的功能,这里不作扩展说明,后续设计模式类的文章会介绍。

这里·额外·说下babel转码后的代码里有 这种括号const a = (m, n, v)运算符。

var a = function(v) {
  return function(v){
    return v + 'v';
  }
}

var m;
var b = (m = a('v'), m('wf'), ''); // ''

以上代码最终返回空, ()里会依次从前到后计算,最后返回最后一个值!!! 是不是有点像reduce,不过人家无法传递参数,只能指定固定的表达式。

decorator引入React

react的decorator其实是利用高阶组件来完成的:

const Log = (WrappedCompoent) => class extends React.Component {
  // 扩展的业务逻辑
  // 可访问App的this
  render() {
    return ();
  }
}

@Log
class App extends React.Component {
// 业务逻辑
  constructor(props) {
    super(props);
    // 可以取到this.props.otherProps
  }
}

在不影响App组件的正常情况下,App中还可以获取高级组件中定义的方法和属性,同时高阶组件也能做额外的一些事情,特别方便。

Tags:ES S7 7D DE 
作者:网络 来源:xhydwufu的博