国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專(zhuān)欄INFORMATION COLUMN

裝飾器與元數(shù)據(jù)反射(1)方法裝飾器

xiaochao / 2279人閱讀

摘要:使用裝飾器的方法很簡(jiǎn)單在裝飾器名前加字符,寫(xiě)在想要裝飾的方法上,類(lèi)似寫(xiě)注釋的方式裝飾器實(shí)際上是一個(gè)函數(shù),入?yún)樗b飾的方法,返回值為裝飾后的方法。經(jīng)過(guò)裝飾過(guò)的方法,它依然按照原來(lái)的方式執(zhí)行,只是額外執(zhí)行了附件的裝飾器函數(shù)的功能。

讓我來(lái)深入地了解一下TypeScript對(duì)于裝飾器模式的實(shí)現(xiàn),以及反射與依賴(lài)注入等相關(guān)特性。

Typescript的源代碼中,可以看到裝飾器能用來(lái)修飾class,property,method,parameter

declare type ClassDecorator = (target: TFunction) => TFunction | void;
declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void;
declare type MethodDecorator = (target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor) => TypedPropertyDescriptor | void;
declare type ParameterDecorator = (target: Object, propertyKey: string | symbol, parameterIndex: number) => void;

接下來(lái)深入地了解一下每種裝飾器:

方法裝飾器

首先來(lái)根據(jù)上面的標(biāo)識(shí),實(shí)現(xiàn)一個(gè)名為log的方法裝飾器。使用裝飾器的方法很簡(jiǎn)單:在裝飾器名前加@字符,寫(xiě)在想要裝飾的方法上,類(lèi)似寫(xiě)注釋的方式:

class C {
    @log
    foo(n: number) {
        return n * 2;
    }
}

裝飾器實(shí)際上是一個(gè)函數(shù),入?yún)樗b飾的方法,返回值為裝飾后的方法。在使用之前需要提前實(shí)現(xiàn)這個(gè)裝飾器函數(shù),如下:

function log(target: Function, key: string, descriptor: any) {
    // target === C.prototype
    // key === "foo"
    // descriptor === Object.getOwnPropertyDescriptor(C.prototype, "foo")
    // 保存對(duì)原方法的引用,避免重寫(xiě)
    var originalMethod = descriptor.value; 

    descriptor.value =  function (...args: any[]) {
        // 將“foo”函數(shù)的參數(shù)列表轉(zhuǎn)化為字符串
        var a = args.map(a => JSON.stringify(a)).join();
        // 調(diào)用 foo() 并獲取它的返回值
        var result = originalMethod.apply(this, args);
        // 將返回的結(jié)果轉(zhuǎn)成字符串
        var r = JSON.stringify(result);
        // 打印日志
        console.log(`Call: ${key}(${a}) => ${r}`);
        // 返回調(diào)用 foo 的結(jié)果
        return result;
    }

    // 返回已編輯的描述符
    return descriptor;
}

該裝飾器函數(shù)包含三個(gè)參數(shù):

target:所要修飾的方法。

key:被修飾方法的名字。

descriptor:屬性描述符,如果為給定可以通過(guò)調(diào)用Object.getOwnPropertyDescriptor()來(lái)獲取。

我們觀(guān)察到,類(lèi)C中使用的裝飾器函數(shù)log并沒(méi)有顯式的參數(shù)傳遞,不免好奇它所需要的參數(shù)是如何傳遞的?以及該函數(shù)是如何被調(diào)用的?

TypeScript最終還是會(huì)被編譯為JavaScript執(zhí)行,為了搞清上面的問(wèn)題,我們來(lái)看一下TypeScript編譯器將類(lèi)C的定義最終生成的JavaScript代碼:

var C = (function () {
    function C() {
    }
    C.prototype.foo = function (n) {
        return n * 2;
    };
    Object.defineProperty(C.prototype, "foo",
        __decorate([
            log
        ], C.prototype, "foo", Object.getOwnPropertyDescriptor(C.prototype, "foo")));
    return C;
})();

而為添加裝飾器所生成的JavaScript代碼如下:

var C = (function () {
    function C() {
    }
    C.prototype.foo = function (n) {
        return n * 2;
    };
    return C;
})();

對(duì)比兩者發(fā)現(xiàn)使用裝飾的不同,只是在類(lèi)定義中,多了如下代碼:

Object.defineProperty(
  __decorate(
    [log],                                              // 裝飾器
    C.prototype,                                        // target:C的原型
    "foo",                                              // key:裝飾器修飾的方法名
    Object.getOwnPropertyDescriptor(C.prototype, "foo") // descriptor
  );
);

通過(guò)查詢(xún)MDN文檔,可以知悉defineProperty的作用:

Object.defineProperty()方法可直接在一個(gè)對(duì)象上定義一個(gè)新的屬性,或者修改對(duì)象上一個(gè)已有的屬性,然后返回這個(gè)對(duì)象。

TypeScript編譯器通過(guò)defineProperty方法重寫(xiě)了所修飾的方法foo,新方法的實(shí)現(xiàn)是由函數(shù)__decorate返回的,那么問(wèn)題來(lái)了:__decorate函數(shù)在哪聲明的呢?

掘地三尺不難找到,來(lái)一起把玩一下:

var __decorate = this.__decorate || function (decorators, target, key, desc) {
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
    return Reflect.decorate(decorators, target, key, desc);
  }
  switch (arguments.length) {
    case 2: 
      return decorators.reduceRight(function(o, d) { 
        return (d && d(o)) || o; 
      }, target);
    case 3: 
      return decorators.reduceRight(function(o, d) { 
        return (d && d(target, key)), void 0; 
      }, void 0);
    case 4: 
      return decorators.reduceRight(function(o, d) { 
        return (d && d(target, key, o)) || o; 
      }, desc);
  }
};

第一行使用了或操作符(||),以確保如果函數(shù)__decorate已被創(chuàng)建,他將不會(huì)被重寫(xiě)。

if (typeof Reflect === "object" && typeof Reflect.decorate === "function")

第二行是一個(gè)條件語(yǔ)句,使用了JavaScript的一個(gè)新特性:元數(shù)據(jù)反射。這個(gè)主題后續(xù)再展開(kāi)講述,下面我們先聚焦觀(guān)察下該新特性的兼容方案:

switch (arguments.length) { 
  case 2: 
    return decorators.reduceRight(function(o, d) { 
      return (d && d(o)) || o; 
    }, target);
  case 3: 
    return decorators.reduceRight(function(o, d) { 
      return (d && d(target, key)), void 0; 
    }, void 0);
  case 4: 
    return decorators.reduceRight(function(o, d) { 
      return (d && d(target, key, o)) || o; 
    }, desc);
}

此處__decorate函數(shù)接受了4個(gè)參數(shù),所以case 4將被執(zhí)行。平心而論這塊代碼有點(diǎn)生澀,沒(méi)關(guān)系掰開(kāi)揉碎了看。

reduceRight方法接受一個(gè)函數(shù)作為累加器和數(shù)組的每個(gè)值(從右到左)將其減少為單個(gè)值。

為了方便理解,上面的代碼重寫(xiě)如下:

[log].reduceRight(function(log, desc) { 
  if(log) {
    return log(C.prototype, "foo", desc);
  }
  else {
    return desc;
  }
}, Object.getOwnPropertyDescriptor(C.prototype, "foo"));

可以看到當(dāng)這段代碼執(zhí)行的時(shí)候,裝飾器函數(shù)log被調(diào)用,并且參數(shù)C.prototype"foo"previousValue也被傳入,如此之前的問(wèn)題現(xiàn)在可以解答了。
經(jīng)過(guò)裝飾過(guò)的foo方法,它依然按照原來(lái)的方式執(zhí)行,只是額外執(zhí)行了附件的裝飾器函數(shù)log的功能。

const c = new C();
const r = c.foo(23); //  "Call: foo(23) => 46"
console.log(r);    // 46

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/101503.html

相關(guān)文章

  • 裝飾與元數(shù)據(jù)反射(3)參數(shù)裝飾

    摘要:可見(jiàn)參數(shù)裝飾器函數(shù)需要個(gè)參數(shù)被裝飾類(lèi)的原型,裝飾參數(shù)所屬的方法名,參數(shù)的索引。參數(shù)裝飾器不應(yīng)當(dāng)用來(lái)修改構(gòu)造器方法或?qū)傩缘男袨椋粦?yīng)當(dāng)用來(lái)產(chǎn)生某種元數(shù)據(jù)。一旦元數(shù)據(jù)被創(chuàng)建,我們便可以用其它的裝飾器去讀取它。 之前已經(jīng)分別介紹了方法裝飾器、屬性裝飾器和類(lèi)裝飾器,這篇文章我們來(lái)繼續(xù)關(guān)注這些話(huà)題: 參數(shù)裝飾器 裝飾器工廠(chǎng) 我們將圍繞以下這個(gè)例子,來(lái)探討這些概念: class Person...

    Barry_Ng 評(píng)論0 收藏0
  • 裝飾與元數(shù)據(jù)反射(2)屬與類(lèi)性裝飾

    摘要:值得注意的是,的返回值復(fù)寫(xiě)了原始的構(gòu)造函數(shù),原因是類(lèi)裝飾器必須返回一個(gè)構(gòu)造器函數(shù)。原始構(gòu)造函數(shù)的原型被復(fù)制給的原型,以確保在創(chuàng)建一個(gè)的新實(shí)例時(shí),操作符如愿以?xún)敚唧w原因可參考鄙人另一篇文章原型與對(duì)象。 上一篇文章中,我們討論了TypeScript源碼中關(guān)于方法裝飾器的實(shí)現(xiàn),搞明白了如下幾個(gè)問(wèn)題: 裝飾器函數(shù)是如何被調(diào)用的? 裝飾器函數(shù)參數(shù)是如何傳入的? __decorate函數(shù)干了...

    Shisui 評(píng)論0 收藏0
  • 裝飾與元數(shù)據(jù)反射(4)元數(shù)據(jù)反射

    摘要:慶幸的是,已經(jīng)支持反射機(jī)制,來(lái)看看這個(gè)特性吧元數(shù)據(jù)反射可以通過(guò)安裝包來(lái)使用元數(shù)據(jù)反射的若要使用它,我們需要在中設(shè)置為,同時(shí)添加的引用,同時(shí)加載文件。復(fù)雜類(lèi)型序列化的團(tuán)隊(duì)為復(fù)雜類(lèi)型的元數(shù)據(jù)序列化做出了努力。 本篇內(nèi)容包括如下部分: 為什么JavaScript中需要反射 元數(shù)據(jù)反射API 基本類(lèi)型序列化 復(fù)雜類(lèi)型序列化 為什么JavaScript中需要反射? 關(guān)于反射的概念,摘自百度百...

    gaosboy 評(píng)論0 收藏0
  • 流暢的 Python - 5. 裝飾閉包

    摘要:看了這一章,發(fā)現(xiàn)原來(lái)是裝飾器,又一新知識(shí)。期間,裝飾器會(huì)做一些額外的工作。書(shū)中介紹了模塊中的三個(gè)裝飾器。另一個(gè)是,這個(gè)裝飾器把函數(shù)結(jié)果保存了起來(lái),避免傳入相同參數(shù)時(shí)重復(fù)計(jì)算。疊放不奇怪,裝飾器返回的就是函數(shù)或可調(diào)用對(duì)象。 在 Web 框架 Flask 中,最常看到的或許是以@app.route開(kāi)頭的那行代碼。由于還是剛接觸 Flask,所以對(duì)這種語(yǔ)法還不熟悉。看了這一章,發(fā)現(xiàn)原來(lái)是裝飾...

    Markxu 評(píng)論0 收藏0
  • Python裝飾vs裝飾模式

    摘要:希望引以為戒鄭傳裝飾模式如果你了解,你肯定聽(tīng)過(guò)裝飾器模式。在面向?qū)ο笾校b飾模式指動(dòng)態(tài)地給一個(gè)對(duì)象添加一些額外的職責(zé)。就增加一些功能來(lái)說(shuō),裝飾模式比生成子類(lèi)更為靈活。 漫談 如果作為一個(gè)Python入門(mén),不了解Python裝飾器也沒(méi)什么,但是如果作為一個(gè)中級(jí)Python開(kāi)發(fā)人員,如果再不對(duì)python裝飾器熟稔于心的話(huà),那么可能并沒(méi)有量變積累到質(zhì)變。 我以前也看過(guò)很多講python 裝...

    stackvoid 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<