Decorator: 원리 부터 실천 까지 나 는 조금도 헛 되 지 않 아 ~
원문 링크: [Nealyang / personal Blog] ()
ES6 는 더 이상 소개 할 필요 가 없습니다. ES6 이전에 장식 기 는 그다지 중요 하지 않 았 을 수도 있 습 니 다. wrapper 를 한 층 만 넣 으 면 되 기 때 문 입 니 다. 하지만 지금 은 문법 사탕 class 의 등장 으로 여러 가지 유형 에서 공유 하거나 확장 하려 고 할 때 코드 가 복잡 해 지고 유지 하기 어렵 습 니 다.본 격 적 으로 우리 Decorator 의 용도 도
Object.defineProperty
Object. defineProperty 에 대해 간단히 말 하면 이 방법 은 대상 의 속성 을 정확하게 추가 하고 수정 할 수 있다 는 것 이다.
문법
Object.defineProperty(obj,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
이 속성 에 대응 하 는 값 입 니 다.유효한 자 바스 크 립 트 값 (수치, 대상, 함수 등) 일 수 있 습 니 다.기본 값 은 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:
이 단락 은 비교적 간단 하 니 코드 를 직접 보 세 요.
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();
실행 결과:
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 와 일치 하 는 형 삼 을 가지 고 있 습 니 다.
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
@name
@seal
class Person {
sayHello() {
console.log(`hello ,my name is ${this.name}`)
}
}
function name(constructor) {
Object.defineProperty(constructor.prototype,'name',{
value:' '
})
}
new Person().sayHello()
//
function seal(constructor) {
let descriptor = Object.getOwnPropertyDescriptor(constructor.prototype, 'sayHello')
Object.defineProperty(constructor.prototype, 'sayHello', {
...descriptor,
writable: false
})
}
new Person().sayHello = 1;// Cannot assign to read only property 'sayHello' of object '#'
위 에서 mixin 하면 제 가 mixin 을 모 의 해 볼 게 요.
class A {
run() {
console.log(' !')
}
}
class B {
jump() {
console.log(' !')
}
}
@mixin(A, B)
class C {}
function mixin(...args) {
return function (constructor) {
for (const arg of args) {
for (let key of Object.getOwnPropertyNames(arg.prototype)) {
if (key === 'constructor') continue;
Object.defineProperty(constructor.prototype, key, Object.getOwnPropertyDescriptor(arg.prototype, key));
}
}
}
}
let c = new C();
c.jump();
c.run();
// !
// !
지금까지 우 리 는 매우 많은 코드 를 쓴 것 같 습 니 다. 맞습니다.이 편 은 철저하게 Decorator 를 투입 하기 위해 서...그냥 시작...
class 멤버 에서 의 사용
이런 장식 기의 쓰 기 는 우리 가 가장 잘 알 고 있 을 것 이다. 세 개의 인 자 를 받 아들 일 것 이다.
class Model{
//
method1(){}
method2 = ()=>{}
//
static method3(){}
static method4 = ()=>{}
}
method 1 과 method 2 는 인 스 턴 스 구성원 이지 만 method 1 은 prototype 에 존재 합 니 다. method 2 는 예화 대상 이후 에 만 있 습 니 다.
method 3 과 method 4 는 정적 구성원 입 니 다. 이들 의 차 이 는 설명자 의 설정 을 매 거 할 수 있 는 지 여부 입 니 다. 우 리 는 babel 코드 를 통 해 볼 수 있 습 니 다.
상기 코드 는 비교적 복잡 하고 간단 한 것 은 다음 과 같다.
function Model () {
//
this.method2 = function () {}
}
//
Object.defineProperty(Model.prototype, 'method1', {
value: function () {},
writable: true,
enumerable: false, //
configurable: true
})
// ,
Model.method4 = function () {}
//
Object.defineProperty(Model, 'method3', {
value: function () {},
writable: true,
enumerable: false, //
configurable: true
})
이 를 통 해 알 수 있 듯 이 method 2 만 예화 할 때 값 을 부여 합 니 다. 존재 하지 않 는 속성 은 descriptor 가 없 기 때문에 이것 이 바로 Property Decorator 에 대해 세 번 째 매개 변 수 를 전달 하지 않 는 이유 입 니 다. 왜 정적 구성원 도 descriptor 를 전달 하지 않 았 는 지 에 대해 합 리 적 인 설명 을 찾 지 못 했 지만 명확 하 게 사용 하려 면수 동 으로 구 할 수 있 습 니 다.
상기 예제 와 같이 우 리 는 네 명의 구성원 에 게 장식 기 를 추가 한 후에 method 1 과 method 2 의 첫 번 째 매개 변 수 는 Model. prototype 이 고 method 3 과 method 4 의 첫 번 째 매개 변 수 는 Model 이다.
class Model {
//
@instance
method1 () {}
@instance
method2 = () => {}
//
@static
static method3 () {}
@static
static method4 = () => {}
}
function instance(target) {
console.log(target.constructor === Model)
}
function static(target) {
console.log(target === Model)
}
함수, 접근 기, 속성 3 자 장식 기 사용
class Model {
@log1
getData1() {}
@log2
getData2() {}
}
// , value
function log1(tag, name, descriptor) {
return {
...descriptor,
value(...args) {
let start = new Date().valueOf()
try {
return descriptor.value.apply(this, args)
} finally {
let end = new Date().valueOf()
console.log(`start: ${start} end: ${end} consume: ${end - start}`)
}
}
}
}
// 、
function log2(tag, name, descriptor) {
let func = descriptor.value //
// value
descriptor.value = function (...args) {
let start = new Date().valueOf()
try {
return func.apply(this, args)
} finally {
let end = new Date().valueOf()
console.log(`start: ${start} end: ${end} consume: ${end - start}`)
}
}
}
get
set
접두사 함수 입 니 다. 속성 을 제어 하 는 할당, 수치 추출 작업 에 사용 되 며 사용 에 있어 함수 장식 기와 차이 가 없습니다
class Modal {
_name = 'Niko'
@prefix
get name() { return this._name }
}
function prefix(target, name, descriptor) {
return {
...descriptor,
get () {
return `wrap_${this._name}`
}
}
}
console.log(new Modal().name) // wrap_Niko
class Modal {
@prefix
static name1 = 'Niko'
}
function prefix(target, name) {
let descriptor = Object.getOwnPropertyDescriptor(target, name)
Object.defineProperty(target, name, {
...descriptor,
value: `wrap_${descriptor.value}`
})
}
console.log(Modal.name1) // wrap_Niko
하나의 사례 의 속성 에 대해 서 는 직접 수정 하 는 방안 이 없 지만 우 리 는 다른 장식 기 를 결합 하여 곡선 으로 나 라 를 구 할 수 있다.
예 를 들 어, 우 리 는 이름 과 나 이 를 초기 화 하 는 매개 변수 로 입력 한 다음 에 이 두 매개 변수 에 대응 하 는 형식 검 사 를 설정 해 야 합 니 다.
const validateConf = {} //
@validator
class Person {
@validate('string')
name
@validate('number')
age
constructor(name, age) {
this.name = name
this.age = age
}
}
function validator(constructor) {
return class extends constructor {
constructor(...args) {
super(...args)
//
for (let [key, type] of Object.entries(validateConf)) {
if (typeof this[key] !== type) throw new Error(`${key} must be ${type}`)
}
}
}
}
function validate(type) {
return function (target, name, descriptor) {
//
validateConf[name] = type
}
}
new Person('Niko', '18') // throw new error: [age must be number]
함수 매개 변수 장식 기
const parseConf = {}
class Modal {
@parseFunc
addOne(@parse('number') num) {
return num + 1
}
}
//
function parseFunc (target, name, descriptor) {
return {
...descriptor,
value (...arg) {
//
for (let [index, type] of parseConf) {
switch (type) {
case 'number': arg[index] = Number(arg[index]) break
case 'string': arg[index] = String(arg[index]) break
case 'boolean': arg[index] = String(arg[index]) === 'true' break
}
return descriptor.value.apply(this, arg)
}
}
}
}
//
function parse(type) {
return function (target, name, index) {
parseConf[index] = type
}
}
console.log(new Modal().addOne('10')) // 11
장식 용 용례
log
하나의 방법 에 log 함 수 를 추가 하고 입력 한 인 자 를 검사 합 니 다.
let log = type => {
return (target,name,decorator) => {
const method = decorator.value;
console.log(method);
decorator.value = (...args) => {
console.info(`${type} :${name}(${args}) = ?`);
let result;
try{
result = method.apply(target,args);
console.info(`(${type}) : ${name}(${args}) => ${result}`);
}catch(err){
console.error(`(${type}) : ${name}(${args}) => ${err}`);
}
return result;
}
}
}
class Math {
@log('add')
add(a, b) {
return a + b;
}
}
const math = new Math();
// (add) : add(2,4) => 6
math.add(2, 4);
time
통계 방법 실행 시간:
function time(prefix) {
let count = 0;
return function handleDescriptor(target, key, descriptor) {
const fn = descriptor.value;
if (prefix == null) {
prefix = `${target.constructor.name}.${key}`;
}
if (typeof fn !== 'function') {
throw new SyntaxError(`@time can only be used on functions, not: ${fn}`);
}
return {
...descriptor,
value() {
const label = `${prefix}-${count}`;
count++;
console.time(label);
try {
return fn.apply(this, arguments);
} finally {
console.timeEnd(label);
}
}
}
}
}
debounce
실행 방법 에 대해 떨 림 방지 처 리 를 하 다.
class Toggle extends React.Component {
@debounce(500, true)
handleClick() {
console.log('toggle')
}
render() {
return (
);
}
}
function _debounce(func, wait, immediate) {
var timeout;
return function () {
var context = this;
var args = arguments;
if (timeout) clearTimeout(timeout);
if (immediate) {
var callNow = !timeout;
timeout = setTimeout(function(){
timeout = null;
}, wait)
if (callNow) func.apply(context, args)
}
else {
timeout = setTimeout(function(){
func.apply(context, args)
}, wait);
}
}
}
function debounce(wait, immediate) {
return function handleDescriptor(target, key, descriptor) {
const callback = descriptor.value;
if (typeof callback !== 'function') {
throw new SyntaxError('Only functions can be debounced');
}
var fn = _debounce(callback, wait, immediate)
return {
...descriptor,
value() {
fn()
}
};
}
}
더 많은 코어 - decorators 의 예 뒤에 Nealyang / Personal Blog 에 보충 하고 주석 설명 을 추가 합 니 다.
레 퍼 런 스
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
파이썬 보틀의 기본 7 Navi 데코레이터메뉴와 끝이 되는 라우팅을 할 필요가 있기 때문이다. sample1.py 일반적으로 과 def xxxx() 함수가 쌍으로 코딩됩니다. 또는. sample2.py sample1.py가 더 멋지지만 route 함수로 라...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.