美文网首页
【TypeScript】装饰器

【TypeScript】装饰器

作者: Stephen0 | 来源:发表于2018-09-09 00:15 被阅读0次

    在看VSCODE源码的时候,看到这样一部分代码:

    class ... {
        constructor(
            ...
            @IStorageService private storageService: IStorageService
            ...
        ) {}
    }
    

    疑惑之际,查看一下官方文档:https://www.tslang.cn/docs/handbook/decorators.html#metadata
    如文档所说,这个是TypeScript新加的一个特性:装饰器。装饰器是一个函数,在运行时会被调用。学过C++的朋友都知道,有很多的代码是跑在在main函数被调用之前的。那么这个装饰器是不是类似的特性呢?

    我们先来写个demo.ts,看看其中一个参数装饰器的行为。

    console.log('Begin');
    
    function nameDec(target: Object, propertyKey: string | symbol, parameterIndex: number) {
        console.log(target);
        console.log(propertyKey);
        console.log(parameterIndex)
    }
    
    class Demo {
        constructor(@nameDec private name: string) {
            console.log(name);
        }
    }
    
    new Demo('Demo');
    
    编译:
    tsc --build tsconfig.json
    运行:
    node demo.js
    结果:
    Begin
    [Function: Demo]
    undefined
    0
    Demo
    

    可以看到,装饰器的函数是跑在构造函数前面的。再编译出的demo.js看看装饰器做了什么事情:

    var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
        var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
        if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
        else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
        return c > 3 && r && Object.defineProperty(target, key, r), r;
    };
    var __param = (this && this.__param) || function (paramIndex, decorator) {
        return function (target, key) { decorator(target, key, paramIndex); }
    };
    console.log('Begin');
    function nameDec(target, propertyKey, parameterIndex) {
        console.log(target);
        console.log(propertyKey);
        console.log(parameterIndex);
    }
    var Demo = /** @class */ (function () {
        function Demo(name) {
            this.name = name;
            console.log(name);
        }
        Demo = __decorate([
            __param(0, nameDec)
        ], Demo);
        return Demo;
    }());
    new Demo('Demo');
    

    那么,装饰器有什么运用场景呢?我们来看一个vscode的一段代码:

    ///////////////////////////////////////////////////////
    // file.1
    // 创建参数装饰器IStorageService
    export const ID = 'storageService';
    // createDecorator返回一个函数
    export const IStorageService = createDecorator<IStorageService>(ID);
    export interface IStorageService {
        _serviceBrand: any;
    }
    
    ///////////////////////////////////////////////////////
    // file.2
    // createDecorator实现
    function storeServiceDependency(id: Function, target: Function, index: number, optional: boolean): void {
        // 这里给constructor原型增加了一个_util.DI_DEPENDENCIES成员,同时将保存参数id和index
        if (target[_util.DI_TARGET] === target) {
            target[_util.DI_DEPENDENCIES].push({ id, index, optional });
        } else {
            target[_util.DI_DEPENDENCIES] = [{ id, index, optional }];
            target[_util.DI_TARGET] = target;
        }
    }
    
    export function createDecorator<T>(serviceId: string): { (...args: any[]): void; type: T; } {
    
        if (_util.serviceIds.has(serviceId)) {
            return _util.serviceIds.get(serviceId);
        }
        // createDecorator返回这个函数,函数调用storeServiceDependency
        const id = <any>function (target: Function, key: string, index: number): any {
            if (arguments.length !== 3) {
                throw new Error('@IServiceName-decorator can only be used to decorate a parameter');
            }
            storeServiceDependency(id, target, index, false);
        };
    
        id.toString = () => serviceId;
    
        _util.serviceIds.set(serviceId, id);
        return id;
    }
    
    ///////////////////////////////////////////////////////
    // file.3
    // 在构造函数中使用参数装饰器IStorageService
    export class SuggestMemories {
    ...
    constructor(
        mode: MemMode,
        // 装饰_storageService
        @IStorageService private readonly _storageService: IStorageService
    ) {
        this._persistSoon = new RunOnceScheduler(() => this._flush(), 3000);
        this.setMode(mode);
    }
    ...
    }
    
    ///////////////////////////////////////////////////////
    // file.4
    // 使用createInstance创建SuggestMemories实例,只需传第一个参数
    this._memory = _instantiationService.createInstance(SuggestMemories, this._editor.getConfiguration().contribInfo.suggestSelection);
    
    ///////////////////////////////////////////////////////
    // file.5
    // createInstance实现
    private _createInstance<T>(desc: SyncDescriptor<T>, args: any[]): T {
    
    // 收集没有被装饰的参数
    let staticArgs = desc.staticArguments.concat(args);
    
    // 通过id查找被装饰的参数
    let serviceDependencies = _util.getServiceDependencies(desc.ctor).sort((a, b) => a.index - b.index);
    let serviceArgs: any[] = [];
    for (const dependency of serviceDependencies) {
        let service = this._getOrCreateServiceInstance(dependency.id);
        if (!service && this._strict && !dependency.optional) {
            throw new Error(`[createInstance] ${desc.ctor.name} depends on UNKNOWN service ${dependency.id}.`);
        }
        serviceArgs.push(service);
    }
    ...
    // ctor构造函数
    const argArray = [desc.ctor];
    // 两者加起来刚好是ctor的全部需要的参数
    argArray.push(...staticArgs);
    argArray.push(...serviceArgs);
    // 调用构造函数创建对象
    // 这里调用了Function.apply
    return <T>create.apply(null, argArray);
    }
    

    通俗点来说,就是利用TypeScript的弱类型特性和装饰器特性,实现了一个加强版默认参数。

    相关文章

      网友评论

          本文标题:【TypeScript】装饰器

          本文链接:https://www.haomeiwen.com/subject/kfcrgftx.html