JS的裝飾器

什么是裝飾器

在面向?qū)ο螅∣OP)的設(shè)計模式中,decorator被稱為裝飾模式。我們在程序開發(fā)中,許多時候并不希望某個類天生就非常龐大,一次性包含許多職責。裝飾者模式可以動態(tài)地給某個對象添加一些額外的功能,而不會影響從這個類中派生的其他對象。

JS的裝飾器

為了更簡便的使用裝飾器模式,ES7引入了一個新的語法,依賴于 ES5 的Object.defineProperty方法。

修飾器的用法

類的裝飾
class A {}

// 等同于

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

也就是說,裝飾器是一個對類進行處理的函數(shù)。裝飾器函數(shù)的第一個參數(shù),就是所要修飾的目標類。
如果覺得一個參數(shù)不夠用,可以在裝飾器外面再封裝一層函數(shù)。

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

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

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

簡單來說JS 的裝飾器可以用來“裝飾”三種類型的對象:類的屬性/方法、訪問器、類本身

針對屬性/方法的裝飾器
class Person {
    @readonly
    name() { return `${this.first} ${this.last}` }
}
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);
  • 裝飾器第一個參數(shù)是的原型對象,上例是 Person.prototype,裝飾器的本意是要“裝飾”類的實例,但是這個時候?qū)嵗€沒生成,所以只能去裝飾原型(這不同于類的裝飾,那種情況時target參數(shù)指的是類本身);
  • 第二個參數(shù)是 所要裝飾的屬性名
  • 第三個參數(shù)是 該屬性的描述對象
函數(shù)方法的裝飾

由于存在函數(shù)提升,使得修飾器不能用于函數(shù)。類是不會提升的,所以就沒有這方面的問題。
可以采用高階函數(shù)的形式直接執(zhí)行。

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);
// decorator 外部可以包裝一個函數(shù),函數(shù)可以帶參數(shù)
function Decorator(type){
    /**
     * 這里是真正的 decorator
     * @target 裝飾的屬性所述的類的原型,注意,不是實例后的類。如果裝飾的是 Car 的某個屬性,這個 target 的值就是 Car.prototype
     * @name 裝飾的屬性的 key
     * @descriptor 裝飾的對象的描述對象
     */
    return function (target, name, descriptor){
        // 以此可以獲取實例化的時候此屬性的默認值
        let v = descriptor.initializer && descriptor.initializer.call(this);
        // 返回一個新的描述對象,或者直接修改 descriptor 也可以
        return {
            enumerable: true,
            configurable: true,
            get: function() {
                return v;
            },
            set: function(c) {
                v = c;
            }
        }
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容