成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

JS 裝飾器,一篇就夠

learning / 551人閱讀

摘要:的裝飾器中的同樣借鑒了這個(gè)語法糖,不過依賴于的方法。等同于也就是說,裝飾器是一個(gè)對(duì)類進(jìn)行處理的函數(shù)。別名或裝飾器在控制臺(tái)顯示一條警告,表示該方法將廢除。有了裝飾器,就可以改寫上面的代碼。

更多文章,請(qǐng)?jiān)贕ithub blog查看

在 ES6 中增加了對(duì)類對(duì)象的相關(guān)定義和操作(比如 class 和 extends ),這就使得我們?cè)诙鄠€(gè)不同類之間共享或者擴(kuò)展一些方法或者行為的時(shí)候,變得并不是那么優(yōu)雅。這個(gè)時(shí)候,我們就需要一種更優(yōu)雅的方法來幫助我們完成這些事情。

什么是裝飾器 Python 的裝飾器

在面向?qū)ο螅∣OP)的設(shè)計(jì)模式中,decorator被稱為裝飾模式。OOP的裝飾模式需要通過繼承和組合來實(shí)現(xiàn),而Python除了能支持 OOP 的 decorator 外,直接從語法層次支持 decorator。

如果你熟悉 python 的話,對(duì)它一定不會(huì)陌生。那么我們先來看一下 python 里的裝飾器是什么樣子的吧:

def decorator(f):
    print "my decorator"
    return f
@decorator
def myfunc():
    print "my function"
myfunc()
# my decorator
# my function

這里的 @decorator 就是我們說的裝飾器。在上面的代碼中,我們利用裝飾器給我們的目標(biāo)方法執(zhí)行前打印出了一行文本,并且并沒有對(duì)原方法做任何的修改。代碼基本等同于:

def decorator(f):
    def wrapper():
        print "my decorator"
        return f()
    return wrapper
def myfunc():
    print "my function"
myfunc = decorator(myfuc)

通過代碼我們也不難看出,裝飾器 decorator 接收一個(gè)參數(shù),也就是我們被裝飾的目標(biāo)方法,處理完擴(kuò)展的內(nèi)容以后再返回一個(gè)方法,供以后調(diào)用,同時(shí)也失去了對(duì)原方法對(duì)象的訪問。當(dāng)我們對(duì)某個(gè)應(yīng)用了裝飾以后,其實(shí)就改變了被裝飾方法的入口引用,使其重新指向了裝飾器返回的方法的入口點(diǎn),從而來實(shí)現(xiàn)我們對(duì)原函數(shù)的擴(kuò)展、修改等操作。

ES7 的裝飾器

ES7 中的 decorator 同樣借鑒了這個(gè)語法糖,不過依賴于 ES5 的 Object.defineProperty 方法 。

Object.defineProperty

Object.defineProperty() 方法會(huì)直接在一個(gè)對(duì)象上定義一個(gè)新屬性,或者修改一個(gè)對(duì)象的現(xiàn)有屬性, 并返回這個(gè)對(duì)象。

該方法允許精確添加或修改對(duì)象的屬性。通過賦值來添加的普通屬性會(huì)創(chuàng)建在屬性枚舉期間顯示的屬性(for...in 或 Object.keys 方法), 這些值可以被改變,也可以被刪除。這種方法允許這些額外的細(xì)節(jié)從默認(rèn)值改變。默認(rèn)情況下,使用 Object.defineProperty() 添加的屬性值是不可變的。

語法
Object.defineProperty(obj, prop, descriptor)

obj:要在其上定義屬性的對(duì)象。

prop:要定義或修改的屬性的名稱。

descriptor:將被定義或修改的屬性描述符。

返回值:被傳遞給函數(shù)的對(duì)象。

在ES6中,由于 Symbol類型 的特殊性,用 Symbol類型 的值來做對(duì)象的key與常規(guī)的定義或修改不同,而Object.defineProperty 是定義 key為 Symbol 的屬性的方法之一。

屬性描述符

對(duì)象里目前存在的屬性描述符有兩種主要形式:數(shù)據(jù)描述符存取描述符。

數(shù)據(jù)描述符是一個(gè)具有值的屬性,該值可能是可寫的,也可能不是可寫的。

存取描述符是由 getter-setter 函數(shù)對(duì)描述的屬性。

描述符必須是這兩種形式之一;不能同時(shí)是兩者。

數(shù)據(jù)描述符和存取描述符均具有以下可選鍵值:

configurable

當(dāng)且僅當(dāng)該屬性的 configurable 為 true 時(shí),該屬性描述符才能夠被改變,同時(shí)該屬性也能從對(duì)應(yīng)的對(duì)象上被刪除。默認(rèn)為 false。

enumerable

enumerable定義了對(duì)象的屬性是否可以在 for...in 循環(huán)和 Object.keys() 中被枚舉。

當(dāng)且僅當(dāng)該屬性的 enumerable 為 true 時(shí),該屬性才能夠出現(xiàn)在對(duì)象的枚舉屬性中。默認(rèn)為 false。
數(shù)據(jù)描述符同時(shí)具有以下可選鍵值:

value

該屬性對(duì)應(yīng)的值。可以是任何有效的 JavaScript 值(數(shù)值,對(duì)象,函數(shù)等)。默認(rèn)為 undefined。

writable

當(dāng)且僅當(dāng)該屬性的 writable 為 true 時(shí),value 才能被賦值運(yùn)算符改變。默認(rèn)為 false。

存取描述符同時(shí)具有以下可選鍵值:

get

一個(gè)給屬性提供 getter 的方法,如果沒有 getter 則為 undefined。該方法返回值被用作屬性值。默認(rèn)為 undefined。

set

一個(gè)給屬性提供 setter 的方法,如果沒有 setter 則為 undefined。該方法將接受唯一參數(shù),并將該參數(shù)的新值分配給該屬性。默認(rèn)為 undefined。

如果一個(gè)描述符不具有value,writable,get 和 set 任意一個(gè)關(guān)鍵字,那么它將被認(rèn)為是一個(gè)數(shù)據(jù)描述符。如果一個(gè)描述符同時(shí)有(value或writable)和(get或set)關(guān)鍵字,將會(huì)產(chǎn)生一個(gè)異常。
用法 類的裝飾
@testable
class MyTestableClass {
  // ...
}

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

MyTestableClass.isTestable // true

上面代碼中,@testable 就是一個(gè)裝飾器。它修改了 MyTestableClass這 個(gè)類的行為,為它加上了靜態(tài)屬性isTestable。testable 函數(shù)的參數(shù) target 是 MyTestableClass 類本身。

基本上,裝飾器的行為就是下面這樣。

@decorator
class A {}

// 等同于

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

也就是說,裝飾器是一個(gè)對(duì)類進(jìn)行處理的函數(shù)。裝飾器函數(shù)的第一個(gè)參數(shù),就是所要裝飾的目標(biāo)類。

如果覺得一個(gè)參數(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

上面代碼中,裝飾器 testable 可以接受參數(shù),這就等于可以修改裝飾器的行為。

注意,裝飾器對(duì)類的行為的改變,是代碼編譯時(shí)發(fā)生的,而不是在運(yùn)行時(shí)。這意味著,裝飾器能在編譯階段運(yùn)行代碼。也就是說,裝飾器本質(zhì)就是編譯時(shí)執(zhí)行的函數(shù)。

前面的例子是為類添加一個(gè)靜態(tài)屬性,如果想添加實(shí)例屬性,可以通過目標(biāo)類的 prototype 對(duì)象操作。

下面是另外一個(gè)例子。

// mixins.js
export function mixins(...list) {
  return function (target) {
    Object.assign(target.prototype, ...list)
  }
}

// main.js
import { mixins } from "./mixins"

const Foo = {
  foo() { console.log("foo") }
};

@mixins(Foo)
class MyClass {}

let obj = new MyClass();
obj.foo() // "foo"

上面代碼通過裝飾器 mixins,把Foo對(duì)象的方法添加到了 MyClass 的實(shí)例上面。

方法的裝飾

裝飾器不僅可以裝飾類,還可以裝飾類的屬性。

class Person {
  @readonly
  name() { return `${this.first} ${this.last}` }
}

上面代碼中,裝飾器 readonly 用來裝飾“類”的name方法。

裝飾器函數(shù) readonly 一共可以接受三個(gè)參數(shù)。

function readonly(target, name, descriptor){
  // descriptor對(duì)象原來的值如下
  // {
  //   value: specifiedFunction,
  //   enumerable: false,
  //   configurable: true,
  //   writable: true
  // };
  descriptor.writable = false;
  return descriptor;
}

readonly(Person.prototype, "name", descriptor);
// 類似于
Object.defineProperty(Person.prototype, "name", descriptor);

裝飾器第一個(gè)參數(shù)是 類的原型對(duì)象,上例是 Person.prototype,裝飾器的本意是要“裝飾”類的實(shí)例,但是這個(gè)時(shí)候?qū)嵗€沒生成,所以只能去裝飾原型(這不同于類的裝飾,那種情況時(shí)target參數(shù)指的是類本身);

第二個(gè)參數(shù)是 所要裝飾的屬性名

第三個(gè)參數(shù)是 該屬性的描述對(duì)象

另外,上面代碼說明,裝飾器(readonly)會(huì)修改屬性的 描述對(duì)象(descriptor),然后被修改的描述對(duì)象再用來定義屬性。

函數(shù)方法的裝飾

裝飾器只能用于類和類的方法,不能用于函數(shù),因?yàn)榇嬖诤瘮?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);
core-decorators.js

core-decorators.js是一個(gè)第三方模塊,提供了幾個(gè)常見的裝飾器,通過它可以更好地理解裝飾器。

@autobind

autobind 裝飾器使得方法中的this對(duì)象,綁定原始對(duì)象。

@readonly

readonly 裝飾器使得屬性或方法不可寫。

@override

override 裝飾器檢查子類的方法,是否正確覆蓋了父類的同名方法,如果不正確會(huì)報(bào)錯(cuò)。

import { override } from "core-decorators";

class Parent {
  speak(first, second) {}
}

class Child extends Parent {
  @override
  speak() {}
  // SyntaxError: Child#speak() does not properly override Parent#speak(first, second)
}

// or

class Child extends Parent {
  @override
  speaks() {}
  // SyntaxError: No descriptor matching Child#speaks() was found on the prototype chain.
  //
  //   Did you mean "speak"?
}
@deprecate (別名@deprecated)

deprecate 或 deprecated 裝飾器在控制臺(tái)顯示一條警告,表示該方法將廢除。

import { deprecate } from "core-decorators";

class Person {
  @deprecate
  facepalm() {}

  @deprecate("We stopped facepalming")
  facepalmHard() {}

  @deprecate("We stopped facepalming", { url: "http://knowyourmeme.com/memes/facepalm" })
  facepalmHarder() {}
}

let person = new Person();

person.facepalm();
// DEPRECATION Person#facepalm: This function will be removed in future versions.

person.facepalmHard();
// DEPRECATION Person#facepalmHard: We stopped facepalming

person.facepalmHarder();
// DEPRECATION Person#facepalmHarder: We stopped facepalming
//
//     See http://knowyourmeme.com/memes/facepalm for more details.
//
@suppressWarnings

suppressWarnings 裝飾器抑制 deprecated 裝飾器導(dǎo)致的 console.warn() 調(diào)用。但是,異步代碼發(fā)出的調(diào)用除外。

使用場(chǎng)景 裝飾器有注釋的作用
@testable
class Person {
  @readonly
  @nonenumerable
  name() { return `${this.first} ${this.last}` }
}

從上面代碼中,我們一眼就能看出,Person類是可測(cè)試的,而name方法是只讀和不可枚舉的。

React 的 connect

實(shí)際開發(fā)中,React 與 Redux 庫結(jié)合使用時(shí),常常需要寫成下面這樣。

class MyReactComponent extends React.Component {}

export default connect(mapStateToProps, mapDispatchToProps)(MyReactComponent);

有了裝飾器,就可以改寫上面的代碼。裝飾

@connect(mapStateToProps, mapDispatchToProps)
export default class MyReactComponent extends React.Component {}

相對(duì)來說,后一種寫法看上去更容易理解。

新功能提醒或權(quán)限

菜單點(diǎn)擊時(shí),進(jìn)行事件攔截,若該菜單有新功能更新,則彈窗顯示。

/**
 * @description 在點(diǎn)擊時(shí),如果有新功能提醒,則彈窗顯示
 * @param code 新功能的code
 * @returns {function(*, *, *)}
 */
 const checkRecommandFunc = (code) => (target, property, descriptor) => {
    let desF = descriptor.value; 
    descriptor.value = function (...args) {
      let recommandFuncModalData = SYSTEM.recommandFuncCodeMap[code];

      if (recommandFuncModalData && recommandFuncModalData.id) {
        setTimeout(() => {
          this.props.dispatch({type: "global/setRecommandFuncModalData", recommandFuncModalData});
        }, 1000);
      }
      desF.apply(this, args);
    };
    return descriptor;
  };
loading

在 React 項(xiàng)目中,我們可能需要在向后臺(tái)請(qǐng)求數(shù)據(jù)時(shí),頁面出現(xiàn) loading 動(dòng)畫。這個(gè)時(shí)候,你就可以使用裝飾器,優(yōu)雅地實(shí)現(xiàn)功能。

@autobind
@loadingWrap(true)
async handleSelect(params) {
  await this.props.dispatch({
    type: "product_list/setQuerypParams",
    querypParams: params
  });
}

loadingWrap 函數(shù)如下:

export function loadingWrap(needHide) {

  const defaultLoading = (
    
加載中...
); return function (target, property, descriptor) { const raw = descriptor.value; descriptor.value = function (...args) { Toast.info(text || defaultLoading, 0, null, true); const res = raw.apply(this, args); if (needHide) { if (get("finally")(res)) { res.finally(() => { Toast.hide(); }); } else { Toast.hide(); } } }; return descriptor; }; }

問題:這里大家可以想想看,如果我們不希望每次請(qǐng)求數(shù)據(jù)時(shí)都出現(xiàn) loading,而是要求只要后臺(tái)請(qǐng)求時(shí)間大于 300ms 時(shí),才顯示loading,這里需要怎么改?

參考

Object.defineProperty()

JavaScript Decorators: What They Are and When to Use Them

ECMAScript 6 入門

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

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

相關(guān)文章

  • 掌握react,這一篇就夠

    摘要:小明小明兒子,可以看到組件顯示了父組件的。小明受控組件和非受控組件受控組件和非受控組件這些都是指的表單組件,當(dāng)一個(gè)表單的值是通過改變的而不是通過是受控組件,否則就是非受控組件。 react眾所周知的前端3大主流框架之一,由于出色的性能,完善的周邊設(shè)施風(fēng)頭一時(shí)無兩。本文就帶大家一起掌握react。 jsx語法 前端MVVM主流框架都有一套自己的模板處理方法,react則使用它獨(dú)特的jsx...

    Enlightenment 評(píng)論0 收藏0
  • python 多個(gè)裝飾的調(diào)用順序

    摘要:如果不使用裝飾器的話,普通的做法可能是在中寫一堆校驗(yàn)代碼來判斷用戶是否登錄,然后決定后面的執(zhí)行邏輯,這樣比較麻煩。 前言 裝飾器是程序開發(fā)中經(jīng)常會(huì)用到的一個(gè)功能,也是python語言開發(fā)的基礎(chǔ)知識(shí),如果能夠在程序中合理的使用裝飾器,不僅可以提高開發(fā)效率,而且可以讓寫的代碼看上去顯的高大上^_^ 使用場(chǎng)景 可以用到裝飾器的地方有很多,簡(jiǎn)單的舉例如以下場(chǎng)景 引入日志 函數(shù)執(zhí)行時(shí)間統(tǒng)計(jì) 執(zhí)...

    wapeyang 評(píng)論0 收藏0
  • CSS 選擇一篇就夠

    摘要:摘自簡(jiǎn)介選擇器是規(guī)則的一部分且位于聲明塊前。選擇器選擇器由哈希磅符號(hào)組成,后面是給定元素的名稱。通用選擇器通用選擇是最終的王牌。子選擇器允許您選擇一個(gè)元素,該元素是另一個(gè)元素的直接子元素。該選擇器僅選擇屬性被賦值為的所有元素。 摘自 MDN web docs 簡(jiǎn)介 選擇器是 CSS 規(guī)則的一部分且位于 CSS 聲明塊前。 showImg(https://segmentfault.com...

    Songlcy 評(píng)論0 收藏0
  • python中的裝飾

    摘要:的裝飾器是用來裝飾函數(shù)的。簡(jiǎn)單裝飾器裝飾器的語法糖是使用符號(hào)表示,裝飾器本身也是一個(gè)函數(shù),只不過參數(shù)是函數(shù)而已。保留函數(shù)的元信息被修飾之后的函數(shù),它的元信息都消失,被替換的函數(shù)代替。中提供了來保存函數(shù)的元信息。 python的裝飾器是用來裝飾函數(shù)的。這是什么意思呢?假如我們有一個(gè)函數(shù),這個(gè)函數(shù)的功能不能滿足我們現(xiàn)有的需求,那么我們可以通過裝飾器在這個(gè)函數(shù)執(zhí)行前執(zhí)行后做一些我們需要的操作...

    張金寶 評(píng)論0 收藏0
  • 你真的完全了解Java動(dòng)態(tài)代理嗎?看這篇就夠

    摘要:動(dòng)態(tài)地代理,可以猜測(cè)一下它的含義,在運(yùn)行時(shí)動(dòng)態(tài)地對(duì)某些東西代理,代理它做了其他事情。所以動(dòng)態(tài)代理的內(nèi)容重點(diǎn)就是這個(gè)。所以下一篇我們來細(xì)致了解下的到底是怎么使用動(dòng)態(tài)代理的。 之前講了《零基礎(chǔ)帶你看Spring源碼——IOC控制反轉(zhuǎn)》,本來打算下一篇講講Srping的AOP的,但是其中會(huì)涉及到Java的動(dòng)態(tài)代理,所以先單獨(dú)一篇來了解下Java的動(dòng)態(tài)代理到底是什么,Java是怎么實(shí)現(xiàn)它的。 ...

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

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

0條評(píng)論

閱讀需要支付1元查看
<