Working environment:
OS: Linux 4.4.0-104-generic #127-Ubuntu SMP Mon Dec 11 12:16:42 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
nodejs: 8.9.1
typescript: 2.6.2
<< class decorator >>
(() => {
///////////////////////////////////////////////////////////////////////////////
//
// Class decorator
//
///////////////////////////////////////////////////////////////////////////////
/**
* See ClassDecorator type at lib.es5.d.ts
* @param ctor constructor function. Prototype of constructor function
* is object for class-definition.
*/
function log<T extends Function>(ctor: T): T | void {
// decorator is called with 'this' bound to global-object,
console.assert(undefined !== this.console);
console.assert(console === this.console);
console.assert('function' === typeof(ctor));
console.log('%o', ctor);
let newConstructor: any = function (...args: Array<any>) {
console.log('New: ' + ctor.name);
let o: any = new (<any>ctor)(...args);
// this is new constructor. So, '_test' is shown at console.log(<instance>)
o._test = 'mytest';
return o;
};
return newConstructor;
}
@log
class Person {
constructor(
public name: string,
public surname: string
) { }
get myname() {
return this.name;
}
fullname(nick: string) {
return `${this.name} ${this.surname} : ${nick}`;
}
}
let p = new Person('name', 'surname');
// console.log('%o, %O', p, p);
console.assert(p.name === 'name');
console.assert(p.myname === 'name');
console.assert(p.fullname('N') === 'name surname : N');
console.assert((<any>p)._test === 'mytest');
///////////////////////////////////////////////////////////////////////////////
//
// Class decorator with argument.
//
///////////////////////////////////////////////////////////////////////////////
function logArg(nick: string) {
return function<T extends Function>(ctor: T): T | void {
let newCtor: any = function (...args: Array<any>) {
// console.log('New: ' + fconstructor.name);
let o: any = new (<any>ctor)(...args);
o.nick = nick;
return o;
};
return newCtor;
};
}
@logArg('nick')
class Person2 {
constructor(
public name: string
) { }
fullname(age: number) {
return `${this.name} ${(<any>this).nick} : ${age}`;
}
}
let p2 = new Person2('name');
// console.log(p);
console.assert(p2.name === 'name');
console.assert(p2.fullname(18) === 'name nick : 18');
console.assert((<any>p2).nick === 'nick');
///////////////////////////////////////////////////////////////////////////////
})();
<< method decorator >>
(() => {
///////////////////////////////////////////////////////////////////////////////
//
// Member decorator
//
///////////////////////////////////////////////////////////////////////////////
/**
* See MethodDecorator at lib.es5.d.ts for detail types.
* @param target class(prototype of class-constructor) where this method belongs to
* @param key name of method
* @param descriptor method(function)-property descriptor
*/
function log(
target: any, key: string | symbol, descriptor: TypedPropertyDescriptor<any>
): TypedPropertyDescriptor<any> | void {
// decorator is called with 'this' bound to global-object,
console.assert(undefined !== this.console);
console.assert(console === this.console);
console.assert('fullname' === key);
// This is ONLY FOR DEMO and EXAMPLE! - NOT RECOMMENDED!
// Add new property to prototype of class-constructor.
// (applied to all class instance!)
target['newattr'] = 'hello';
// save a reference to the original method this way we keep the values currently in the
// descriptor and don't overwrite what another decorator might have done to the descriptor.
if (descriptor === undefined) {
descriptor = Object.getOwnPropertyDescriptor(target, key)!;
}
//editing the descriptor/value parameter
let orig = descriptor.value;
descriptor.value = function () {
console.log(`Called: ${key}`);
return orig!.apply(this, arguments);
};
return descriptor;
}
class Person {
constructor(
public name: string,
) { }
get myname() {
return this.name;
}
@log
fullname(newname: string, nick: string) {
this.name = newname;
return `${this.name}:${nick}`;
}
}
let p = new Person('name');
let p2 = new Person('name2');
// console.log(p.fullname('newname', 'nick'));
console.assert('name' === p.myname);
console.assert('name2' === p2.myname);
console.assert('hello' === (<any>p).newattr);
console.assert('hello' === (<any>p2).newattr);
console.assert('newname:nick' === p.fullname('newname', 'nick'));
console.assert('newname' === p.myname);
console.assert(`hello` === (<any>p).newattr);
console.log(p);
///////////////////////////////////////////////////////////////////////////////
//
// Member decorator with argument
//
///////////////////////////////////////////////////////////////////////////////
// Same way with class decorator.
///////////////////////////////////////////////////////////////////////////////
})();
<< parameter decorator >>
(() => {
///////////////////////////////////////////////////////////////////////////////
//
// Parameter decorator
//
///////////////////////////////////////////////////////////////////////////////
/**
*
* @param target class(prototype of class-constructor) where this method belongs in
* @param key name of method where this parameter belongs to
* @param index parameter position in function - stars at 0(the first parameter).
*/
function log(target: any, key: string | symbol, index: number): void {
// decorator is called with 'this' bound to global-object,
console.assert(undefined !== this.console);
console.assert(console === this.console);
console.assert('fullname' === key);
console.assert(1 === index);
}
class Person {
constructor(
public name: string,
) { }
get myname() {
return this.name;
}
fullname(newname: string, @log nick: string) {
this.name = newname;
return `${this.name}:${nick}`;
}
}
let p = new Person('name');
// console.log(p.fullname('newname', 'nick'));
console.assert('newname:nick' === p.fullname('newname', 'nick'));
console.assert('newname' === p.myname);
///////////////////////////////////////////////////////////////////////////////
})();
<< property decorator >>
(() => {
///////////////////////////////////////////////////////////////////////////////
//
// Property decorator
//
///////////////////////////////////////////////////////////////////////////////
/**
*
* @param target class(prototype of class-constructor) where this method belongs in
* @param key name of method where this parameter belongs to
*/
function log(target: any, key: string | symbol): void {
// decorator is called with 'this' bound to global-object,
console.assert(undefined !== this.console);
console.assert(console === this.console);
console.assert('name' === key || 'obj' === key);
let internalKey = `$${key}`;
// property getter
let getter = function () {
console.log(`Get: ${key} => ${this[internalKey]}`);
return this[internalKey];
};
// property setter
let setter = function (newVal: any) {
console.log(`Set: ${key} => ${newVal}`);
this[internalKey] = newVal;
};
// Delete property.
if (delete target[key]) {
console.assert(undefined === target[`$${key}`]);
// Create new property with getter and setter
Object.defineProperty(target, key, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
}
}
class Person {
@log
public name = 'init';
@log
public obj = {};
constructor(name: string) {
this.name = name;
}
get myname() {
return this.name;
}
fullname(newname: string, nick: string) {
this.name = newname;
return `${this.name}:${nick}`;
}
}
let p = new Person('name');
p.name = 'hello';
p.obj = {a: 1};
console.assert((<any>p)['$name'] === p.name);
console.assert((<any>p)['$obj'] === p.obj);
console.assert('newname:nick' === p.fullname('newname', 'nick'));
console.assert('newname' === p.myname);
// console.log('%o', p);
///////////////////////////////////////////////////////////////////////////////
})();
'Language > Javascript' 카테고리의 다른 글
[NodeJs] Overhead of Promise(async/await) (more serious than you think!) (0) | 2019.05.16 |
---|---|
[Jest] Sharing code and symbolic link 문제. (0) | 2018.06.26 |
[NodeJS] Kill child process executed via 'exec' and 'spawn' (0) | 2017.12.28 |
[Javascript] Generator 를 이용해서 Callback hell을 벗어나 보기.... (0) | 2017.12.28 |
[Javascript] Understanding 'Prototype' of Javascript (0) | 2017.12.28 |