>  기사  >  웹 프론트엔드  >  JavaScript 데코레이터 기능 사용법 요약

JavaScript 데코레이터 기능 사용법 요약

青灯夜游
青灯夜游앞으로
2018-10-09 14:51:331569검색

이 글은 JS 데코레이터 기능에 대한 관련 사용법과 지식 포인트를 요약한 것입니다. 도움이 필요한 친구들이 참고할 수 있기를 바랍니다.

ES6에서는 클래스 객체(예: 클래스 및 확장)의 관련 정의 및 작업이 추가되어 여러 다른 클래스 간에 일부 메서드나 동작을 공유하거나 확장할 수 있습니다. 현재 이러한 작업을 수행하는 데 도움이 되는 보다 우아한 방법이 필요합니다.

데코레이터란 무엇인가요? 🎜#객체 지향(OOP) 디자인 패턴에서는 데코레이터를 데코레이션 패턴이라고 합니다. OOP의 장식 모드는 상속과 조합을 통해 구현되어야 하며 Python은 OOP의 장식자를 지원하는 것 외에도 구문 수준에서 직접 장식자를 지원합니다.

파이썬에 익숙하신 분들이라면 익숙하실 겁니다. 먼저 Python의 데코레이터가 어떻게 생겼는지 살펴보겠습니다.

def decorator(f):
  print "my decorator"
  return f
@decorator
def myfunc():
  print "my function"
myfunc()
# my decorator
# my function
여기서 @designator는 우리가 데코레이터라고 부르는 것입니다. 위 코드에서는 대상 메서드를 실행하기 전에 데코레이터를 사용하여 텍스트 한 줄을 인쇄하고 원래 메서드를 수정하지 않습니다. 코드는 기본적으로 다음과 같습니다:
def decorator(f):
  def wrapper():
    print "my decorator"
    return f()
  return wrapper
def myfunc():
  print "my function"
myfunc = decorator(myfuc)

데코레이터가 우리가 데코레이트할 대상 메소드인 매개변수를 수신하는 것을 코드에서 보는 것은 어렵지 않습니다. 나중에 호출하면 원래 메소드 객체에 대한 액세스도 손실됩니다. 함수에 장식을 적용할 때 실제로 장식된 메서드의 항목 참조를 변경하여 데코레이터가 반환한 메서드의 진입점을 다시 가리키도록 함으로써 원래 함수를 확장하고 수정할 수 있습니다.

ES7 데코레이터

ES7의 데코레이터도 이 구문을 사용하지만 ES5의 Object.defineProperty 메서드를 사용합니다. Object.defineProperty() 메소드는 객체의 새 속성을 직접 정의하거나 객체의 기존 속성을 수정하고 이 객체를 반환합니다.

이 방법을 사용하면 개체의 속성을 정확하게 추가하거나 수정할 수 있습니다. 할당에 의해 추가된 일반 속성은 속성 열거(for...in 또는 Object.keys 메서드) 중에 노출되는 속성을 생성하며 이러한 값은 변경되거나 삭제될 수 있습니다. 이 접근 방식을 사용하면 이러한 추가 세부 정보를 기본값에서 변경할 수 있습니다. 기본적으로 Object.defineProperty()를 사용하여 추가된 속성 값은 변경할 수 없습니다.

Syntax

Object.defineProperty(obj, prop, descriptor)

obj: 속성이 정의될 개체입니다.

prop: 정의하거나 수정할 속성의 이름입니다.

descriptor: 정의되거나 수정될 속성 설명자입니다.

반환 값: 함수에 전달된 개체입니다.
  1. ES6에서는 Symbol 유형의 특수성으로 인해 Symbol 유형의 값을 객체의 키로 사용하는 것이 기존의 정의나 수정과는 다르며, Object .defineProperty는 키를 Symbol의 속성으로 정의하는 방법 중 하나입니다.
  2. 속성 설명자

  3. 현재 객체에 존재하는 속성 설명자의 두 가지 주요 형태는 데이터 설명자와 저장소입니다. 설명자 가져오기 .

  4. 데이터 설명자는 쓸 수 있거나 쓸 수 없는 값을 가진 속성입니다.
액세스 설명자는 getter-setter 함수 쌍으로 설명되는 속성입니다.

설명자는 동시에 두 가지 형식 중 하나여야 합니다.

데이터 설명자와 액세스 설명자 모두 다음과 같은 선택적 키 값을 갖습니다.


configurable

# 🎜 🎜#
    속성의 configurable이 true인 경우에만 속성 설명자가 변경될 수 있으며 해당 개체에서 속성을 삭제할 수도 있습니다. 기본값은 거짓입니다.
  • enumerable
  • enumerable은 객체의 속성을 for...in 루프 및 Object.keys() 에서 열거할 수 있는지 여부를 정의합니다.

  • 속성의 열거 가능 값이 true인 경우에만 해당 속성이 객체의 열거 속성에 나타날 수 있습니다. 기본값은 거짓입니다.
데이터 설명자에는 다음과 같은 선택적 키 값도 있습니다.

value

이 속성에 해당하는 값입니다. 유효한 JavaScript 값(숫자, 객체, 함수 등)이 될 수 있습니다. 기본값은 정의되지 않았습니다.

writable

속성 쓰기 가능 여부가 true인 경우에만 할당 연산자로 값을 변경할 수 있습니다. 기본값은 거짓입니다.

액세스 설명자에는 다음과 같은 선택적 키 값도 있습니다.

get

속성을 제공하는 getter getter가 없으면 메소드가 정의되지 않습니다. 이 메소드의 반환 값은 속성 값으로 사용됩니다. 기본값은 정의되지 않았습니다.

set

속성에 대한 setter를 제공하는 메서드입니다. setter가 없으면 정의되지 않습니다. 이 메서드는 고유한 매개변수를 받아들이고 매개변수의 새 값을 속성에 할당합니다. 기본값은 정의되지 않았습니다.

설명자에 값, 쓰기 가능, 가져오기 및 설정 키워드가 없으면 데이터 설명자로 간주됩니다. 설명자에 (value 또는 writable) 및 (get 또는 set) 키워드가 모두 있으면 예외가 생성됩니다.

사용법

类的装饰

@testable
class MyTestableClass {
 // ...
}

function testable(target) {
 target.isTestable = true;
}

MyTestableClass.isTestable // true

上面代码中,@testable 就是一个装饰器。它修改了 MyTestableClass这 个类的行为,为它加上了静态属性isTestable。testable 函数的参数 target 是 MyTestableClass 类本身。

基本上,装饰器的行为就是下面这样。

@decorator
class A {}

// 等同于

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

也就是说,装饰器是一个对类进行处理的函数。装饰器函数的第一个参数,就是所要装饰的目标类。

如果觉得一个参数不够用,可以在装饰器外面再封装一层函数。

function testable(isTestable) {
 return function(target) {
  target.isTestable = isTestable;
 }
}

@testable(true)
class MyTestableClass {}
MyTestableClass.isTestable // true

@testable(false)
class MyClass {}
MyClass.isTestable // false

上面代码中,装饰器 testable 可以接受参数,这就等于可以修改装饰器的行为。

注意,装饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,装饰器能在编译阶段运行代码。也就是说,装饰器本质就是编译时执行的函数。

前面的例子是为类添加一个静态属性,如果想添加实例属性,可以通过目标类的 prototype 对象操作。

下面是另外一个例子。

// mixins.js
export function mixins(...list) {
 return function (target) {
  Object.assign(target.prototype, ...list)
 }
}

// main.js
import { mixins } from './mixins'

const Foo = {
 foo() { console.log('foo') }
};

@mixins(Foo)
class MyClass {}

let obj = new MyClass();
obj.foo() // 'foo'

上面代码通过装饰器 mixins,把Foo对象的方法添加到了 MyClass 的实例上面。

方法的装饰

装饰器不仅可以装饰类,还可以装饰类的属性。

class Person {
 @readonly
 name() { return `${this.first} ${this.last}` }
}

上面代码中,装饰器 readonly 用来装饰“类”的name方法。

装饰器函数 readonly 一共可以接受三个参数。

function readonly(target, name, descriptor){
 // descriptor对象原来的值如下
 // {
 //  value: specifiedFunction,
 //  enumerable: false,
 //  configurable: true,
 //  writable: true
 // };
 descriptor.writable = false;
 return descriptor;
}

readonly(Person.prototype, 'name', descriptor);
// 类似于
Object.defineProperty(Person.prototype, 'name', descriptor);
  • 装饰器第一个参数是 类的原型对象,上例是 Person.prototype,装饰器的本意是要“装饰”类的实例,但是这个时候实例还没生成,所以只能去装饰原型(这不同于类的装饰,那种情况时target参数指的是类本身);

  • 第二个参数是 所要装饰的属性名

  • 第三个参数是 该属性的描述对象

另外,上面代码说明,装饰器(readonly)会修改属性的 描述对象(descriptor),然后被修改的描述对象再用来定义属性。

函数方法的装饰

装饰器只能用于类和类的方法,不能用于函数,因为存在函数提升。

另一方面,如果一定要装饰函数,可以采用高阶函数的形式直接执行。

function doSomething(name) {
 console.log('Hello, ' + name);
}

function loggingDecorator(wrapped) {
 return function() {
  console.log('Starting');
  const result = wrapped.apply(this, arguments);
  console.log('Finished');
  return result;
 }
}

const wrapped = loggingDecorator(doSomething);

core-decorators.js

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

@autobind

autobind 装饰器使得方法中的this对象,绑定原始对象。

@readonly

readonly 装饰器使得属性或方法不可写。

@override

override 装饰器检查子类的方法,是否正确覆盖了父类的同名方法,如果不正确会报错。

import { override } from 'core-decorators';

class Parent {
 speak(first, second) {}
}

class Child extends Parent {
 @override
 speak() {}
 // SyntaxError: Child#speak() does not properly override Parent#speak(first, second)
}

// or

class Child extends Parent {
 @override
 speaks() {}
 // SyntaxError: No descriptor matching Child#speaks() was found on the prototype chain.
 //
 //  Did you mean "speak"?
}

@deprecate (别名@deprecated)

deprecate 或 deprecated 装饰器在控制台显示一条警告,表示该方法将废除。

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() {}
}

let 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.
//

@suppressWarnings

suppressWarnings 装饰器抑制 deprecated 装饰器导致的 console.warn() 调用。但是,异步代码发出的调用除外。

使用场景

装饰器有注释的作用

@testable
class Person {
 @readonly
 @nonenumerable
 name() { return `${this.first} ${this.last}` }
}

有了装饰器,就可以改写上面的代码。装饰

@connect(mapStateToProps, mapDispatchToProps)
export default class MyReactComponent extends React.Component {}

相对来说,后一种写法看上去更容易理解。

新功能提醒或权限

菜单点击时,进行事件拦截,若该菜单有新功能更新,则弹窗显示。

/**
 * @description 在点击时,如果有新功能提醒,则弹窗显示
 * @param code 新功能的code
 * @returns {function(*, *, *)}
 */
 const checkRecommandFunc = (code) => (target, property, descriptor) => {
  let desF = descriptor.value; 
  descriptor.value = function (...args) {
   let recommandFuncModalData = SYSTEM.recommandFuncCodeMap[code];

   if (recommandFuncModalData && recommandFuncModalData.id) {
    setTimeout(() => {
     this.props.dispatch({type: 'global/setRecommandFuncModalData', recommandFuncModalData});
    }, 1000);
   }
   desF.apply(this, args);
  };
  return descriptor;
 };

loading

在 React 项目中,我们可能需要在向后台请求数据时,页面出现 loading 动画。这个时候,你就可以使用装饰器,优雅地实现功能。

@autobind
@loadingWrap(true)
async handleSelect(params) {
 await this.props.dispatch({
  type: 'product_list/setQuerypParams',
  querypParams: params
 });
}

loadingWrap 函数如下:

export function loadingWrap(needHide) {

 const defaultLoading = (
  <p className="toast-loading">
   <Loading className="loading-icon"/>
   <p>加载中...</p>
  </p>
 );

 return function (target, property, descriptor) {
  const raw = descriptor.value;
  
  descriptor.value = function (...args) {
   Toast.info(text || defaultLoading, 0, null, true);
   const res = raw.apply(this, args);
   
   if (needHide) {
    if (get(&#39;finally&#39;)(res)) {
     res.finally(() => {
      Toast.hide();
     });
    } else {
     Toast.hide();
    }
   }
  };
  return descriptor;
 };
}

问题:这里大家可以想想看,如果我们不希望每次请求数据时都出现 loading,而是要求只要后台请求时间大于 300ms 时,才显示loading,这里需要怎么改?

总结:以上就是本篇文的全部内容,希望能对大家的学习有所帮助。更多相关教程请访问JavaScript视频教程

相关推荐:

php公益培训视频教程

JavaScript图文教程

JavaScript在线手册

위 내용은 JavaScript 데코레이터 기능 사용법 요약의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 jb51.net에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제