关于javascript:js装饰器入门讲解

49次阅读

共计 3935 个字符,预计需要花费 10 分钟才能阅读完成。

参考

  • js 装璜器 @Decorator
  • 装璜器 - 阮一峰

提醒

  • TypeScript 曾经残缺的实现了装璜器,js 装璜器则是一个尚在提案中的语法,如果应用 js 而非 ts,则须要配置 Babel 能力应用
  • 浏览此文前须要理解的前置常识:js 类,Object.defineProperty,js 原型,闭包

罕用的装璜器举例

咱们抛开装璜器是怎么实现的,先来看两个装璜器的例子,以便对装璜器有初步的理解

示例一:readonly

class Person {constructor(name) {this.name = name}
  getName() { return this.name}
}

下面的代码定义了一个类 Person,它有一个原型办法 getName,显然 getName 是能够被批改的

Person.prototype.getName = fuction() { return ` 哈哈哈 `}
console.log(new Person('test').getName()); // 哈哈哈 

如果我当初要求 getName 不能被批改,应用装璜器能够达到成果

// 这是 readonly 的具体实现,能够先疏忽,前面会具体介绍
function readonly(target, name, descriptor) {descriptor.writable = false;}

class Person {constructor(name) {this.name = name;}
  @readonly
  getName() {return this.name;}
}
// 上面语句会报错,提醒 getName readonly
Person.prototype.getName = function () {return ` 哈哈哈 `;};
console.log(new Person('test').getName());

下面的代码,咱们给 getName 办法的定义下面加了一行 @readonly,这就是装璜器的写法,十分直观,就像正文一样,一眼就看出 getName 这个办法是只读的,不能批改

示例二:deprecate

当咱们调用第三方库的办法的时候,经常会在控制台看到一些正告提醒这个办法行将被移除,应用装璜器能够达到这个成果

import {deprecate} from 'core-decorators'; // 是一个第三方模块,提供了几个常见的装璜器

class Person {
  @deprecate
  facepalm() {}

  @deprecate('We stopped facepalming')
  facepalmHard() {}

  @deprecate('We stopped facepalming', { url: 'http://knowyourmeme.com/memes/facepalm'})
  facepalmHarder() {}
}

const person = new Person();
person.facepalm();
// DEPRECATION Person#facepalm: This function will be removed in future versions.
person.facepalmHard();
// DEPRECATION Person#facepalmHard: We stopped facepalming
person.facepalmHarder();
// DEPRECATION Person#facepalmHarder: We stopped facepalming
//
//     See http://knowyourmeme.com/memes/facepalm for more details.
//

当咱们调用被 deprecate 装璜器装璜的办法 facepalm 时,控制台显示一条正告,示意该办法将破除,能够给 deprecate 传入不同的参数管制显示内容

通过下面的两个例子,咱们能够看出,装璜器优雅的扭转了类原来的行为,它是可复用的而且表白直观

装璜器是什么

装璜器实质是一种函数,写成 @ + 函数名,它能够减少或批改类的性能

装璜器的用法

首先明确装璜器并不能在代码的任意地位应用,它只能用于类,要么放在类定义后面,要么放在类属性的后面

作用于类的装璜器(放在类定义的后面)

基本上,装璜器的行为就是上面这样

@decorator
class A {}

// 等同于

class A {}
A = decorator(A) || A;

也就是说,装璜器是一个对类进行解决的函数。装璜器函数的第一个参数,就是所要装璜的指标类。

示例三:应用装璜器给类增加动态属性

// 定义一个装璜器函数
const addPropertyA = (target) => {
  // 此处的 target 为类自身(即 ClassA)target.a = 'a'; // 给类(ClassA)增加一个动态属性
};

@addPropertyA
class ClassA {constructor() {this.a = 1;}
}
console.info('ClassA.a:', ClassA.a); // a
console.info('a:', new ClassA().a); // 1

tips:能够应用在线编译 ts 将下面的 ts 代码编译成的 js(es2017)帮忙了解

示例四:应用装璜器给类增加实例属性

本例给类 ClassA 增加了原型办法

const addMethodTest = (target) => {target.prototype.test = () => {console.log('test');
  };
};

@addMethodTest
class ClassA {}

new ClassA().test(); // test

示例五:传参的类装璜器

示例三中,咱们给 ClassA 这个类增加了一个动态属性 a, 它的值是字符串 ’a’(一个写死的字面量),如果属性 a 的值须要由参数传入呢?此时须要在装璜器外再封装一层函数

function addPropertyA(value) {return function (target) {target.a = value;};
}
// 箭头函数写法
// const addPropertyA = (value) => (target) => {
//   target.a = value;
// };

@addPropertyA('test')
class ClassA {}
console.info('ClassA.a:', ClassA.a); // test

下面的写法等同于

function getAddPropertyA(value) {return function (target) {target.a = value;};
}
const addPropertyA = getAddPropertyA('test');

@addPropertyA
class ClassA {}
console.info('ClassA.a:', ClassA.a); // test

作用于类属性的装璜器(放在类属性定义的后面)

示例六:readonly

再来看文章结尾的示例一,咱们有一个 Person 类

class Person {constructor(name) {this.name = name}
  getName() { return this.name}
}

如果我当初要求 getName 不能被批改,能够改用 Object.defineProperty 来定义 getName

class Person {constructor(name: string) {this.name = name;}
}
Object.defineProperty(Person.prototype, 'getName', {
  writable: false,
  value() {return this.name;},
});
// 上面语句会报错,提醒 getName readonly
Person.prototype.getName = function () {return ` 哈哈哈 `;};
console.log(new Person('test').getName());
function readonly(target, name, descriptor) {descriptor.writable = false;}

class Person {constructor(name) {this.name = name;}
  @readonly
  getName() {return this.name;}
}
// 上面语句会报错,提醒 getName readonly
Person.prototype.getName = function () {return ` 哈哈哈 `;};
console.log(new Person('test').getName());

以上两种写法达到了同样的成果,然而第二种显著更优雅

readonly 是装璜器函数,第一个参数是类的原型对象,上例是 Person.prototype(留神不同于作用于类的装璜器),第二个参数是所要装璜的属性名(上例是 getName),第三个参数是该属性的形容对象(同 Object.defineProperty 中传入的形容对象)。

下面代码阐明,装璜器(readonly)能够批改属性的形容对象(descriptor),而后被批改的形容对象再用来定义属性

示例七:实例属性装璜器

装璜器不仅能够作用于类办法,也能够作用于一般的类属性

import {deprecate} from 'core-decorators'; // 是一个第三方模块,提供了几个常见的装璜器

class Person {@readonly name = 'Jack';}

const person = new Person();
person.name = 'Rose'; // TypeError: Cannot assign to read only property 'name' of object '#<Person>'

正文完
 0