设为首页 加入收藏

TOP

Decorator:从原理到实践(一)
2019-09-25 11:18:26 】 浏览:114
Tags:Decorator: 原理 实践

前言

原文链接:Nealyang/personalBlog

img

ES6 已经不必在过多介绍,在 ES6 之前,装饰器可能并没有那么重要,因为你只需要加一层 wrapper 就好了,但是现在,由于语法糖 class 的出现,当我们想要去在多个类之间共享或者扩展一些方法的时候,代码会变得错综复杂,难以维护,而这,也正式我们 Decorator 的用武之地。

Object.defineProperty

关于 Object.defineProperty 简单的说,就是该方法可以精准的添加和修改对象的属性

语法

Object.defineProperty(obj,prop,descriptor)

  • ojb:要在其上定义属性的对象
  • prop:要定义或修改的属性的名称
  • descriptor:将被定义或修改的属性描述符

该方法返回被传递给函数的对象

在ES6中,由于 Symbol类型的特殊性,用Symbol类型的值来做对象的key与常规的定义或修改不同,而Object.defineProperty 是定义key为Symbol的属性的方法之一。

通过赋值操作添加的普通属性是可枚举的,能够在属性枚举期间呈现出来(for...in 或 Object.keys 方法), 这些属性的值可以被改变,也可以被删除。这个方法允许修改默认的额外选项(或配置)。默认情况下,使用 Object.defineProperty() 添加的属性值是不可修改的

属相描述符

对象里目前存在的属性描述符有两种主要形式:数据描述符存取描述符。数据描述符是一个具有值的属性,该值可能是可写的,也可能不是可写的。存取描述符是由getter-setter函数对描述的属性。描述符必须是这两种形式之一;不能同时是两者。

数据描述符和存取描述符均具有以下可选键值:

configurable

当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false

enumerable

当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中。默认为 false。

数据描述符同时具有以下可选键值:

value

该属性对应的值。可以是任何有效的 java script 值(数值,对象,函数等)。默认为 undefined。

writable

当且仅当该属性的writable为true时,value才能被赋值运算符改变。默认为 false

存取描述符同时具有以下可选键值:

get

一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。当访问该属性时,该方法会被执行,方法执行时没有参数传入,但是会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象)。默认为 undefined。

set

一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值。默认为 undefined。

如果一个描述符不具有value,writable,get 和 set 任意一个关键字,那么它将被认为是一个数据描述符。如果一个描述符同时有(value或writable)和(get或set)关键字,将会产生一个异常

更多使用实例和介绍,参看:MDN

装饰者模式

在看Decorator之前,我们先看下装饰者模式的使用,我们都知道,装饰者模式能够在不改变对象自身基础上,在程序运行期间给对象添加指责。特点就是不影响之前对象的特性,而新增额外的职责功能。

like...this:

IMAGE

这段比较简单,直接看代码吧:

let Monkey = function () {}
Monkey.prototype.say = function () {
  console.log('目前我只是个野猴子');
}
let TensionMonkey = function (monkey) {
  this.monkey = monkey;
}
TensionMonkey.prototype.say = function () {
  this.monkey.say();
  console.log('带上紧箍咒,我就要忘记世间烦恼!');
}
let monkey = new TensionMonkey(new Monkey());
monkey.say();

执行结果:
IMAGE

Decorator

Decorator其实就是一个语法糖,背后其实就是利用es5的Object.defineProperty(target,name,descriptor),了解Object.defineProperty请移步这个链接:MDN文档

其背后原理大致如下:

class Monkey{
  say(){
    console.log('目前,我只是个野猴子');
  }
}

执行上面的代码,大致代码如下:

Object.defineProperty(Monkey.prototype,'say',{
  value:function(){console.log('目前,我只是个野猴子')},
  enumerable:false,
  configurable:true,
  writable:true
})

如果我们利用装饰器来修饰他

class Monkey{
@readonly
say(){console.log('现在我是只读的了')}
}

在这种装饰器的属性,会在Object.defineProperty为Monkey.prototype注册say属性之前,执行以下代码:

let descriptor = {
  value:specifiedFunction,
  enumerable:false,
  configurable:true,
  writeable:true
};

descriptor = readonly(Monkey.prototype,'say',descriptor)||descriptor;
Object.defineProperty(Monkey.prototype,'say',descriptor);

从上面的伪代码我们可以看出,Decorator只是在Object.defineProperty为Monkey.prototype注册属性之前,执行了一个装饰函数,其属于一个类对Object.defineProperty的拦截。所以它和Object.defineProperty具有一致的形参:

  • obj:作用的目标对象
  • prop:作用的属性名
  • descriptor:针对该属性的描述符

下面看下简单的使用

在class中的使用

  • 创建一个新的class继承自原有的class,并添加属性
@name
class Person{
  sayHello(){
    console.log(`hello ,my name is ${this.name}`)
  }
}

function name(constructor) {  
  return class extends constructor{
    name="Nealyang"
  }
}

new Person().sayHello()
//hello ,my name is Nealyang
  • 针对当前c
首页 上一页 1 2 3 4 下一页 尾页 1/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇JavaScript 数据类型转换表 下一篇JS高级程序设计笔记

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目