欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript裝飾器的實現(xiàn)原理詳解

 更新時間:2022年10月13日 16:12:19   作者:青天訣  
最近在使用TS+Vue的開發(fā)模式,發(fā)現(xiàn)項目中大量使用了裝飾器,看得我手足無措,今天特意研究一下實現(xiàn)原理,方便自己理解這塊知識點,有需要的小伙伴也可以參考一下

最近在使用TS+Vue的開發(fā)模式,發(fā)現(xiàn)項目中大量使用了裝飾器,看得我手足無措,今天特意研究一下實現(xiàn)原理,方便自己理解這塊知識點。

裝飾器的常見作用

  • 裝飾一個類的屬性
  • 裝飾一個類

裝飾器只能針對類和類的屬性,不能直接作用于函數(shù),因為存在函數(shù)提升。

下面我們針對這兩種情況進行舉例闡述。

裝飾類的屬性

function readonly(target, name, descriptor) {
    discriptor.writable = false;
    return discriptor;
}
class Cat {
    @readonly
    say() {
        console.log("meow ~");
    }
}
var kitty = new Cat();
kitty.say = function() {
    console.log("woof !");
}
kitty.say()    // meow ~

ES6中的類實際上就是一個語法糖,本質上是構造函數(shù),類的屬性的定義使用的是 Object.defineProperty() 類的代碼可以改寫為下面代碼:

class Cat {
    say() {
        console.log("meow ~");
    }
}

// 上面等價于下面代碼

function Cat() {}

Object.defineProperty(Cat.prototype, "say", {
    value: function() { console.log("meow ~"); },
    enumerable: false,
    configurable: true,
    writable: true
});

say方法前面加入@readonly后,整個代碼等價于下面代碼:

function readonly(target, name, descriptor) {
    discriptor.writable = false;
    return discriptor;
}

function Cat() {}

let descriptor = {
    value: function() { console.log("meow ~"); },
    enumerable: false,
    configurable: true,
    writable: true
};

descriptor = readonly(Cat.prototype, 'say', descriptor) || descriptor;

Object.defineProperty(Cat.prototype, "say", descriptor);

裝飾類屬性的大致實現(xiàn)原理,就是上面的語法糖實現(xiàn)過程。

這里注意下裝飾器的用法,是在屬性前面加入@readonly,不是@readonly()。如果是后一種寫法的話,裝飾器函數(shù)需要返回一個函數(shù)來表示裝飾器。這種寫法主要是通過傳參來擴展裝飾器函數(shù)的功能,使裝飾器函數(shù)復用性更強。

如通過傳參決定是否設置只讀,代碼如下:

function readonly(flag) {
    return function (target, name, descriptor) {
        if (flag) {
            discriptor.writable = false;
        } else {
            discriptor.writable = true;
        }
        return discriptor;
    }
}
class Cat {
    @readonly(true)
    say() {
        console.log("meow ~");
    }
}
var kitty = new Cat();
kitty.say = function() {
    console.log("woof !");
}
kitty.say()

裝飾類

裝飾一個類的時候類本身本質上是一個函數(shù),沒有descriptor,target是這個函數(shù)本身。

function isAnimal(target) {
    target.isAnimal = true;
  	return target;
}
@isAnimal
class Cat {
    ...
}
console.log(Cat.isAnimal);    // true

也就是說,上面的@isAnimal其實就是做了下面這件事

Cat = isAnimal(function Cat() { ... });

注意

作用在方法上的 decorator 接收的第一個參數(shù)(target )是類的 prototype;如果把一個 decorator 作用到類上,則它的第一個參數(shù) target 是 類本身。

裝飾器執(zhí)行的時間是在屬性定義的時候,也就是被裝飾的屬性在定義后就是已經(jīng)被裝飾器處理過的不一樣的屬性了。

下面舉例看下裝飾器的執(zhí)行時間:

function log(message) {
    return function() {
        console.log(message);
    }
}
console.log('before class');
@log('class Bar')
class Bar {
    @log('class method bar');
    bar() {}
    @log('class getter alice');
    get alice() {}
    @log('class property bob');
    bob = 1;
}
console.log('after class');
let bar = {
    @log('object method bar')
    bar() {}
};

輸出結果:

before class
class method bar
class getter alice
class property bob
class Bar
after class
object method bar

可以看出裝飾器在類和類的屬性定義的時候就對它們進行了"裝飾"。

實例應用

我們的示例場景是這樣的

  • 首先創(chuàng)建一個普通的Man類,它的抵御值 2,攻擊力為 3,血量為 3;
  • 然后我們讓其帶上鋼鐵俠的盔甲,這樣他的抵御力增加 100,變成 102;
  • 讓其帶上光束手套,攻擊力增加 50,變成 53;
  • 最后讓他增加“飛行”能力

1. 創(chuàng)建 Man 類:

class Man{
  constructor(def = 2,atk = 3,hp = 3){
    this.init(def,atk,hp);
  }

  init(def,atk,hp){
    this.def = def; // 防御值
    this.atk = atk;  // 攻擊力
    this.hp = hp;  // 血量
  }
  toString(){
    return `防御力:${this.def},攻擊力:${this.atk},血量:${this.hp}`;
  }
}

var tony = new Man();

console.log(`當前狀態(tài) ===> ${tony}`); 

// 輸出:當前狀態(tài) ===> 防御力:2,攻擊力:3,血量:3

2. 裝飾器為鋼鐵俠裝配盔甲:

function decorateArmour(target, key, descriptor) {
  const method = descriptor.value;
  let moreDef = 100;
  let ret;
  descriptor.value = (...args)=>{
    args[0] += moreDef;
    ret = method.apply(target, args);
    return ret;
  }
  return descriptor;
}

class Man{
  constructor(def = 2,atk = 3,hp = 3){
    this.init(def,atk,hp);
  }

  @decorateArmour
  init(def,atk,hp){
    this.def = def; // 防御值
    this.atk = atk;  // 攻擊力
    this.hp = hp;  // 血量
  }
  toString(){
    return `防御力:${this.def},攻擊力:${this.atk},血量:${this.hp}`;
  }
}

var tony = new Man();

console.log(`當前狀態(tài) ===> ${tony}`);
// 輸出:當前狀態(tài) ===> 防御力:102,攻擊力:3,血量:3

3. 裝飾器增加光束手套

function decorateLight(target, key, descriptor) {
  const method = descriptor.value;
  let moreAtk = 50;
  let ret;
  descriptor.value = (...args)=>{
    args[1] += moreAtk;
    ret = method.apply(target, args);
    return ret;
  }
  return descriptor;
}

class Man{
  constructor(def = 2,atk = 3,hp = 3){
    this.init(def,atk,hp);
  }

  @decorateArmour
  @decorateLight
  init(def,atk,hp){
    this.def = def; // 防御值
    this.atk = atk;  // 攻擊力
    this.hp = hp;  // 血量
  }
...
}
var tony = new Man();
console.log(`當前狀態(tài) ===> ${tony}`);
//輸出:當前狀態(tài) ===> 防御力:102,攻擊力:53,血量:3

在這里你就能看出裝飾模式的優(yōu)勢了,它可以對某個方法進行疊加使用,對原類的侵入性非常小,只是增加一行@decorateLight而已,可以方便地增刪;(同時還可以復用)

4. 增加飛行能力

function addFly(canFly){
  return function(target){
    target.prototype.canFly = canFly;
    let extra = canFly ? '(技能加成:飛行能力)' : '';
    let method = target.prototype.toString;
    target.prototype.toString = (...args)=>{
      return method.apply(target.prototype,args) + extra;
    }
    return target;
  }
}

@addFly(true)
class Man{
  constructor(def = 2,atk = 3,hp = 3){
    this.init(def,atk,hp);
  }

  @decorateArmour
  @decorateLight
  init(def,atk,hp){
    this.def = def; // 防御值
    this.atk = atk;  // 攻擊力
    this.hp = hp;  // 血量
  }
  ...
}
...

console.log(`當前狀態(tài) ===> ${tony}`);
// 輸出:當前狀態(tài) ===> 防御力:102,攻擊力:53,血量:3(技能加成:飛行能力)

到此這篇關于JavaScript裝飾器的實現(xiàn)原理詳解的文章就介紹到這了,更多相關JavaScript裝飾器內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論