成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

[譯]Mixin 函數(shù)

zxhaaa / 2322人閱讀

摘要:函數(shù)通常是面向?qū)ο缶幊田L(fēng)格,具有副作用。因為在函數(shù)式編程中,很有可能這些引用指向的并不是同一個對象。記住,函數(shù)并不意味著函數(shù)式編程。函數(shù)可以用函數(shù)式編程風(fēng)格編寫,避免副作用并不修改參數(shù),但這并不保證。

軟件構(gòu)建系列

原文鏈接:Functional Mixins
譯者注:在編程中,mixin 類似于一個固有名詞,可以理解為混合或混入,通常不進(jìn)行直譯,本文也是同樣。

這是“軟件構(gòu)建”系列教程的一部分,該系列主要從 JavaScript ES6+ 中學(xué)習(xí)函數(shù)式編程,以及軟件構(gòu)建技術(shù)。敬請關(guān)注。
上一篇 | 第一篇

Mixin 函數(shù) 是指能夠給對象添加屬性或行為,并可以通過管道連接在一起的組合工廠函數(shù),就如同流水線上的工人。Mixin 函數(shù)不依賴或要求一個基礎(chǔ)工廠或構(gòu)造函數(shù):簡單地將任意一個對象傳入一個 mixin,就會得到一個增強之后的對象。

Mixin 函數(shù)的特點:

數(shù)據(jù)封裝

繼承私有狀態(tài)

多繼承

覆蓋重復(fù)屬性

無需基礎(chǔ)類

動機

現(xiàn)代軟件開發(fā)的核心就是組合:我們將一個龐大復(fù)雜的問題,分解成更小,更簡單的問題,最終將這些問題的解決辦法組合起來就變成了一個應(yīng)用程序。

組合的最小單位就是以下兩者之一:

函數(shù)

數(shù)據(jù)結(jié)構(gòu)

他們的組合就定義了應(yīng)用的結(jié)構(gòu)。

通常,組合對象由類繼承實現(xiàn),其中子類從父類繼承其大部分功能,并擴(kuò)展或覆蓋部分。這種方法導(dǎo)致了 is-a 問題,比如:管理員是一名員工,這引發(fā)了許多設(shè)計問題:

高耦合:由于子類的實現(xiàn)依賴于父類,所以類繼承是面向?qū)ο笤O(shè)計中最緊密的耦合。

脆弱的子類:由于高耦合,對父類的修改可能會破壞子類。軟件作者可能在不知情的情況下破壞了第三方管理的代碼。

層次不靈活:根據(jù)單一祖先分類,隨著長時間的演變,最終所有的類都將不適用于新用例。

重復(fù)問題:由于層次不靈活,新用例通常是通過重復(fù)而不是擴(kuò)展來實現(xiàn)的,這導(dǎo)致不同的類有著相似的類結(jié)構(gòu)。而一旦重復(fù)創(chuàng)建,在創(chuàng)建其子類時,該繼承自哪個類以及為什么繼承于這個類就不清晰了。

大猩猩和香蕉問題:“...面向?qū)ο笳Z言的問題是他們會獲得所有與之相關(guān)的隱含環(huán)境。比如你想要一個香蕉,但你得到的會是一只拿著香蕉的大猩猩,以及一整片叢林?!?- Joe Armstrong(Coders at Work)

假設(shè)管理員是一名員工,你如何處理聘請外部顧問暫時行使管理員職務(wù)的情況?(譯者:木知啊~)如果你事先知道所有的需求,類繼承可能有效,但我從沒有看到過這種情況。隨著不斷地使用,新問題和更有效的流程將會被發(fā)現(xiàn),應(yīng)用程序和需求不可避免地隨著時間的推移而發(fā)展和演變。

Mixin 提供了更靈活的方法。

什么是 Mixin?

“組合優(yōu)于繼承?!?- 設(shè)計模式:可重用面向?qū)ο筌浖脑?/p>

Mixin 是對象組合的一種,它將部分特性混入復(fù)合對象中,使得這些屬性成為復(fù)合對象的屬性。

面向?qū)ο缶幊讨械?"mixin" 一詞來源于冰激凌店。不同于將不同口味的冰激凌預(yù)先混合,每個顧客可以自由混合各種口味的冰激凌,從而創(chuàng)造出屬于自己的冰激凌口味。

對象 mixin 與之類似:從一個空對象開始,然后一步步擴(kuò)展它。由于 JavaScript 支持動態(tài)對象擴(kuò)展,所以在 JavaScript 中使用對象 mixin 是非常簡單的。它也是 JavaScript 中最常見的繼承形式,來看一個例子:

const chocolate = {
  hasChocolate: () => true
};
const caramelSwirl = {
  hasCaramelSwirl: () => true
};
const pecans = {
  hasPecans: () => true
};
const iceCream = Object.assign({}, chocolate, caramelSwirl, pecans);
/*
// 支持對象擴(kuò)展符的話也可以寫成這樣...
const iceCream = {...chocolate, ...caramelSwirl, ...pecans};
*/
console.log(`
  hasChocolate: ${ iceCream.hasChocolate() }
  hasCaramelSwirl: ${ iceCream.hasCaramelSwirl() }
  hasPecans: ${ iceCream.hasPecans() }
`);

/* 輸出
  hasChocolate: true
  hasCaramelSwirl: true
  hasPecans: true
*/
什么是函數(shù)繼承?

函數(shù)繼承是指通過函數(shù)來增強對象實例實現(xiàn)特性繼承的過程。該函數(shù)建立一個閉包使得部分?jǐn)?shù)據(jù)是私有的,并通過動態(tài)對象擴(kuò)展使得對象實例擁有新的屬性和方法。

來看一下這個詞的創(chuàng)造者 Douglas Crockford 所給出的例子。

// 父類
function base(spec) {
    var that = {}; // Create an empty object
    that.name = spec.name; // Add it a "name" property
    return that; // Return the object
}
// 子類
function child(spec) {
    // 調(diào)用父類構(gòu)造函數(shù)
    var that = base(spec); 
    that.sayHello = function() { // Augment that object
        return "Hello, I"m " + that.name;
    };
    return that; // Return it
}
// Usage
var result = child({ name: "a functional object" });
console.log(result.sayHello()); // "Hello, I"m a functional object"

由于 child()base() 緊密耦合在一起,當(dāng)你想添加 grandchild(), greatGrandchild() 等時,你將面對類繼承中許多常見的問題。

什么是 Mixin 函數(shù)?

Mixin 函數(shù)是一系列將新的屬性或行為混入特定對象的組合函數(shù)。它不依賴或需要一個基礎(chǔ)工廠方法或構(gòu)造器,只需將任意對象傳入一個 mixin 方法,它就會被擴(kuò)展。

來看下面的例子。

const flying = o => {
  let isFlying = false;
  return Object.assign({}, o, {
    fly () {
      isFlying = true;
      return this;
    },
    isFlying: () => isFlying,
    land () {
      isFlying = false;
      return this;
    }
  });
};
const bird = flying({});
console.log( bird.isFlying() ); // false
console.log( bird.fly().isFlying() ); // true

這里需要注意,當(dāng)調(diào)用 flying() 時需要傳遞一個被擴(kuò)展的對象。Mixin 函數(shù)被設(shè)計用來實現(xiàn)函數(shù)組合,繼續(xù)看下去。

const quacking = quack => o => Object.assign({}, o, {
  quack: () => quack
});
const quacker = quacking("Quack!")({});
console.log( quacker.quack() ); // "Quack!"
組合 Mixin 函數(shù)

通過簡單的函數(shù)組合就可以將 mixin 函數(shù)組合起來。

const createDuck = quack => quacking(quack)(flying({}));
const duck = createDuck("Quack!");
console.log(duck.fly().quack());

但是,這看上去有點丑陋,調(diào)試或重新排列組合順序也有點困難。

當(dāng)然,這只是標(biāo)準(zhǔn)的函數(shù)組合,而我們可以通過一些好的辦法來將它們組合起來,比如 compose()pipe()。如果,使用 pipe() 就需反轉(zhuǎn)函數(shù)的調(diào)用順序,才能保持相同的執(zhí)行順序。當(dāng)屬性沖突時,最后的屬性生效。

const pipe = (...fns) => x => fns.reduce((y, f) => f(y), x);
// OR...
// import pipe from `lodash/fp/flow`;
const createDuck = quack => pipe(
  flying,
  quacking(quack)
)({});
const duck = createDuck("Quack!");
console.log(duck.fly().quack());
Mixin 函數(shù)的使用場景

你應(yīng)當(dāng)總是使用最簡單的抽象來解決問題。從純函數(shù)開始。如果需要一個持久化狀態(tài)的對象,就試試工廠方法。如果你需要構(gòu)建更復(fù)雜的對象,那就試試 Mixin 函數(shù)。

以下是一些使用 Mixin 函數(shù)很棒的例子:

應(yīng)用狀態(tài)管理,比如,Redux

某些橫向服務(wù),比如,集中日志處理

組件生命周期函數(shù)

功能可組合的數(shù)據(jù)類型,比如,JavaScript Array 類實現(xiàn)了 Semigroup, Functor, Foldable

一些代數(shù)結(jié)構(gòu)可以根據(jù)其他代數(shù)結(jié)構(gòu)得出,這意味著新的數(shù)據(jù)類型可以通過某些推導(dǎo)組合而成,而不需要定制。

注意事項

大部分問題都可以使用純函數(shù)優(yōu)雅地解決。然而,mixin 函數(shù)同類繼承一樣,會造成一些問題。事實上,使用 mixin 函數(shù)能夠完全復(fù)制類繼承的優(yōu)缺點。

你應(yīng)當(dāng)遵循以下的建議來避免這些問題。

使用最簡單的實現(xiàn)。從左邊開始,根據(jù)需要移到右邊。純函數(shù) > 工廠方法 > mixin 函數(shù) > 類繼承

避免創(chuàng)建對象,mixin,或數(shù)據(jù)類型之間的 is-a 關(guān)系

避免 mixins 之間的隱含依賴關(guān)系,mixin 函數(shù)應(yīng)當(dāng)是獨立的

mixin 函數(shù)并不意味著函數(shù)式編程

類繼承

在 JavaScript 中,類繼承在極少情況下(也許永遠(yuǎn)不)會是最佳方案,但這通常是一些不由你控制的庫或框架。在這種場景下,類有時是實用的。

無需擴(kuò)展你自己的類(不需要你建立多層次的類結(jié)構(gòu))

無需使用 new 關(guān)鍵字,也就是說,框架會替你實例化

Angular 2+ 和 React 滿足這些需求,所以你無需擴(kuò)展你自己的類,而是放心地使用它們的類。在 React 中,你可以不使用類,不過這樣你的組件將不會獲得 React 的優(yōu)化,并且你的組件也會同文檔中的例子不同。但無論如何,使用函數(shù)構(gòu)建 React 組件總是你的首選。

性能

在一些瀏覽器中,類會獲得 JavaScript 引擎的優(yōu)化,其他的則無法直接使用。在幾乎所有情況下,這些優(yōu)化都不會對程序產(chǎn)生決定性的影響。事實上,在接下去的幾年中,你都無需關(guān)心類在性能上的不同。無論你如何構(gòu)建對象,對象創(chuàng)建和屬性訪問總是非??斓模棵氚偃f次)。

也就是說,類似 RxJS,Lodash 等公共庫的作者應(yīng)該研究使用 class 創(chuàng)建對象實例可能的性能優(yōu)勢。除非你能夠證明通過類能夠解決性能瓶頸,否則,你就應(yīng)當(dāng)使你的代碼保持干凈、靈活,而不必?fù)?dān)心性能。

隱式依賴

你可能打算創(chuàng)建一些計劃用于一同工作的 mixin 函數(shù)。試想一下,你想要為你的應(yīng)用添加一個配置管理器,當(dāng)你訪問不存在的配置屬性時,它會提示警告,像這樣:

// log 模塊
const withLogging = logger => o => Object.assign({}, o, {
  log (text) {
    logger(text)
  }
});

// 確認(rèn)配置項存在模塊,同 log 模塊無關(guān),這里只是確保 log 存在
const withConfig = config => (o = {
  log: (text = "") => console.log(text)
}) => Object.assign({}, o, {
  get (key) {
    return config[key] == undefined ?
      // vvv 隱式依賴! vvv
      this.log(`Missing config key: ${ key }`) :
      // ^^^ 隱式依賴! ^^^
      config[key]
    ;
  }
});
// 模塊封裝
const createConfig = ({ initialConfig, logger }) =>
  pipe(
    withLogging(logger),
    withConfig(initialConfig)
  )({})
;
// 調(diào)用
const initialConfig = {
  host: "localhost"
};
const logger = console.log.bind(console);
const config = createConfig({initialConfig, logger});
console.log(config.get("host")); // "localhost"
config.get("notThere"); // "Missing config key: notThere"

也可以是這樣,

// 引入 log 模塊
import withLogging from "./with-logging";
const addConfig = config => o => Object.assign({}, o, {
  get (key) {
    return config[key] == undefined ? 
      this.log(`Missing config key: ${ key }`) :
      config[key]
    ;
  }
});
const withConfig = ({ initialConfig, logger }) => o =>
  pipe(
    // vvv 明確的依賴! vvv
    withLogging(logger),
    // ^^^ 明確的依賴! ^^^
    addConfig(initialConfig)
  )(o)
;
// 工廠方法
const createConfig = ({ initialConfig, logger }) =>
  withConfig({ initialConfig, logger })({})
;

// 另一模塊
const initialConfig = {
  host: "localhost"
};
const logger = console.log.bind(console);
const config = createConfig({initialConfig, logger});
console.log(config.get("host")); // "localhost"
config.get("notThere"); // "Missing config key: notThere"

選擇隱式還是顯式取決于很多因素。Mixin 函數(shù)作用的數(shù)據(jù)類型必須是有效的,這就需要 API 文檔中的函數(shù)簽名非常清晰。

這就是隱式依賴版本中為 o 添加默認(rèn)值的原因。由于 JavaScript 缺少類型注釋功能,但我們可以通過默認(rèn)值來代替它。

const withConfig = config => (o = {
  log: (text = "") => console.log(text)
}) => Object.assign({}, o, {
  // ...

如果你使用 TypeScript 或 Flow,最好為你的對象參數(shù)定義一個明確的接口。

Mixin 函數(shù)與函數(shù)式編程

Mixin 函數(shù)并不像函數(shù)式編程那樣純。Mixin 函數(shù)通常是面向?qū)ο缶幊田L(fēng)格,具有副作用。許多 Mixin 函數(shù)會改變傳入的參數(shù)對象。注意!

出于同樣的原因,一些開發(fā)者更喜歡函數(shù)式編程風(fēng)格,不修改傳入的對象。在編寫 mixin 時,你應(yīng)當(dāng)適當(dāng)?shù)厥褂眠@兩種編碼風(fēng)格。

這意味著,如果你要返回對象的實例,則始終返回 this,而不是閉包中對象實例的引用。因為在函數(shù)式編程中,很有可能這些引用指向的并不是同一個對象。另外,總是使用 Object.assign(){...object, ...spread} 語法進(jìn)行復(fù)制。但需要注意的是,非枚舉的屬性將不會存在于最終的對象上。

const a = Object.defineProperty({}, "a", {
  enumerable: false,
  value: "a"
});
const b = {
  b: "b"
};
console.log({...a, ...b}); // { b: "b" }

出于同樣的原因,如果你使用的 mixin 函數(shù)不是自己構(gòu)建的,就不要認(rèn)為它就是純的。假設(shè)基礎(chǔ)對象會被改變,假設(shè)它可能會產(chǎn)生副作用,不保證參數(shù)不會改變,即由 mixin 函數(shù)組合而成的記錄工廠通常是不安全的。

結(jié)論

Mixin 函數(shù)是可組合的工廠方法,它能夠為對象添加屬性和行為,就如同裝配線上的站。它是將多個來源的功能(has-a, uses-a, can-do)組合成行為的好方法,而不是從一個類上繼承所有功能(is-a)。

記住,“mixin 函數(shù)” 并不意味著“函數(shù)式編程”。Mixin 函數(shù)可以用函數(shù)式編程風(fēng)格編寫,避免副作用并不修改參數(shù),但這并不保證。第三方 mixin 可能存在副作用和不確定性。

不同于對象 mixin,mixin 函數(shù)支持正真的私有數(shù)據(jù)(封裝),包括繼承私有數(shù)據(jù)的能力。

不同于單繼承,mixin 函數(shù)還支持繼承多個祖先的能力,類似于類裝飾器或多繼承。

不同于 C++ 中的多繼承,JavaScript 中很少出現(xiàn)屬性沖突問題,當(dāng)屬性沖突發(fā)生時,總是最后添加的 mixin 有效。

不同于類裝飾器或多繼承,不需要基類

總是從最簡單的實現(xiàn)方式開始,只根據(jù)需要使用更復(fù)雜的實現(xiàn)方式:

純函數(shù) > 工廠方法 > mixin 函數(shù) > 類繼承

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

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/83670.html

相關(guān)文章

  • 』React Mixin 的使用

    摘要:可以說,相比繼承而已,更喜歡這種組合的方式。需要指出的是,是可以包含在其他的中的程序會在控制臺打印出。包含多個我們的要包裹在數(shù)組當(dāng)中,提醒了我們可以在組件中包含多個注意事項這里有幾件事需要引起我們的注意,當(dāng)我們使用的時候。 update: Mixin 只適用于 ES5。如果你的項目里面用的是 ES6 ,可以采用高階組件來實現(xiàn) Mixin 的功能。 我使用 React.js 構(gòu)建大型項目...

    張巨偉 評論0 收藏0
  • [] 為什么原型繼承很重要

    摘要:使用構(gòu)造函數(shù)的原型繼承相比使用原型的原型繼承更加復(fù)雜,我們先看看使用原型的原型繼承上面的代碼很容易理解。相反的,使用構(gòu)造函數(shù)的原型繼承像下面這樣當(dāng)然,構(gòu)造函數(shù)的方式更簡單。 五天之前我寫了一個關(guān)于ES6標(biāo)準(zhǔn)中Class的文章。在里面我介紹了如何用現(xiàn)有的Javascript來模擬類并且介紹了ES6中類的用法,其實它只是一個語法糖。感謝Om Shakar以及Javascript Room中...

    xiao7cn 評論0 收藏0
  • 學(xué)習(xí) underscore 源碼整體架構(gòu),打造屬于自己的函數(shù)式編程類庫

    摘要:譯立即執(zhí)行函數(shù)表達(dá)式處理支持瀏覽器環(huán)境微信小程序。學(xué)習(xí)整體架構(gòu),利于打造屬于自己的函數(shù)式編程類庫。下一篇文章可能是學(xué)習(xí)的源碼整體架構(gòu)。也可以加微信,注明來源,拉您進(jìn)前端視野交流群。 前言 上一篇文章寫了jQuery整體架構(gòu),學(xué)習(xí) jQuery 源碼整體架構(gòu),打造屬于自己的 js 類庫 雖然看過挺多underscore.js分析類的文章,但總感覺少點什么。這也許就是紙上得來終覺淺,絕知此...

    junnplus 評論0 收藏0
  • 前端利器:SASS基礎(chǔ)與Compass入門

    摘要:在吸取了的一些特性基礎(chǔ)上,有了大幅改進(jìn),也就是現(xiàn)在的。嵌套極大程度上降低了選擇器名稱和屬性的重復(fù)書寫。選擇器嵌套選擇器嵌套是指從一個選擇器中嵌套子選擇器,來實現(xiàn)選擇器的繼承關(guān)系。也已經(jīng)成為的一個標(biāo)配組件。 SASS是Syntactically Awesome Stylesheete Sass的縮寫,它是css的一個開發(fā)工具,提供了很多便利和簡單的語法,讓css看起來更像是一門...

    娣辯孩 評論0 收藏0
  • 基于Mixin Network的Go語言比特幣開發(fā)教程 : 用 Mixin Messenger 機器

    摘要:基于的語言比特幣開發(fā)教程用機器人接受和發(fā)送比特幣在上一篇教程中我們創(chuàng)建了自動回復(fù)消息的機器人當(dāng)用戶發(fā)送消息時,機器人會自動回復(fù)同一條消息按本篇教程后學(xué)習(xí)后完成后,你的機器人將會接受用戶發(fā)送過來的加密貨幣,然后立即轉(zhuǎn)回用戶。 基于Mixin Network的Go語言比特幣開發(fā)教程 : 用 Mixin Messenger 機器人接受和發(fā)送比特幣 showImg(https://segmen...

    Kaede 評論0 收藏0

發(fā)表評論

0條評論

zxhaaa

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<