函數式 JavaScript:將方法從對象中解耦

dyydyy999 7年前發布 | 16K 次閱讀 JavaScript開發 JavaScript

在項目中我一直做的一件事情就是把方法從其對象中解耦。 map 、 filter 以及 reduce 并非是全部,但是它們肯定是首先獲得自由的。

解耦方法可以讓方法擺脫父對象所施加的限制,同時在表示代碼的方式上給了我們更多的自由。

那么這到底是啥玩意呢?

為簡便起見,我們只從 Array 對象中抽取 map 方法。幸運的是 JavaScript 的原型繼承讓這變得很簡單,因為我們想要的功能就在 Array.prototype.map 中。JavaScript 中一個很棒的事情就是我們可以直接調用這個方法,只不過要用 .call 來調用,因為 map 需要一個 this 參數。

揉進點柯里化,一行代碼就能搞定。

const map = f => x => Array.prototype.map.call(x, f)

現在我們就可以在沒有 Array 對象的情況下調用 map 函數了!

調用 map 的替代方式

調用 map 的方式有很多種,由于 V8 引擎的優化,這些調用方式在性能上其實沒有多大差別。

有點性能上的差異沒多大意義,而且這些數字每次都會變。結論應該是這些方法是差不多的。

解耦如何讓我的生活變得更好?

這個問題很好!可以說是最好的問題。我認為這最好用代碼來解釋,而不是空談,所以下面我們就直接用代碼開始好了。

document.querySelectorAll (以及類似的方法)不返回數組,而是返回一個 NodeList 對象,而 NodeList 對象是不包含 map 方法的。雖然你可以采用一些魔法手段,將 NodeList 轉換為 Array,但是這種轉換是沒有必要的。因為 map 可以遍歷 NodeList,就好像它是一個數組一樣。

const items = document.querySelectorAll('div')

items.map(doSomething)
// => Uncaught TypeError: items.map is not a function

map(doSomething)(items)
// => [<div/>, ..., <div/>]

我們甚至可以 map 一個字符串,而不需要先把它轉型為字符數組。

const value = 'Kitty Cat'

value.map(doSomething)
// => Uncaught TypeError: items.map is not a function

map(doSomething)(value)
// => ['K', 'i', 't', 't', 'y', ' ', 'C', 'a', 't']

解耦讓我們可以輕松將一個對象映射轉換為一個列表映射:

const getFullName = ({ first, last }) => `${first} ${last}`

getFullName({ first: 'Max', last: 'Power' })
// => 'Max Power'

map(getFullName)([
  { first: 'Max', last: 'Power' },
  { first: 'Disco', last: 'Stu' },
  { first: 'Joe', last: 'Kickass' }
])
// => ['Max Power', 'Disco Stu', 'Joe Kickass']

我們甚至可以對對象進行 map :

const obj = {
  0: 4,
  1: 5,
  2: 6,
  length: 3
}

map(increase)(obj)
// => [5, 6, 7]

解耦允許我們組合函數:

const mapDoStuff = map(doStuff)
const mapDoSomething = map(doSomething)

// 組合 2 個映射
const mapDoSomethingThenStuff =
  compose(mapDoStuff, mapDoSomething)

解耦(帶柯里化)允許我們偏應用函數參數,創建新函數。

const increaseOne = x => x + 1

// partially applied map increase
const increaseMany = map(increaseOne)

increaseMany([1, 2, 3])
// => [2, 3, 4]

和 this 說再見!!!

const cat = {
  sound: 'meow',
  speak: function() {
    console.log(this.sound)
  }
}

const catSpeak = cat.speak

cat.speak()
// => 'meow'

catSpeak()
// => Uncaught TypeError: Cannot read property 'sound' of undefined

在本例中, cat.speak 運行正常,但是 catSpeak 不行,因為 this 上下文改變了。這太煩了!如果我們把 speak 方法解耦出來,就 再也不用操心 this 了

const cat = { sound: 'meow' }
const speak = ({ sound }) => console.log(sound)

speak(cat)
// => 'meow'

然后我們就可以創建使用解耦過的函數的新函數。

const cat = { sound: 'meow' }
const speak = ({ sound }) => console.log(sound)
const speakLoudly = obj =>
  speak({ ...obj, sound: obj.sound.toUpperCase() + '!' })

speak(cat)
// => 'meow'

speakLoudly(cat)
// => 'MEOW!'

總結

本文學習了解耦方法并將其從對象中抽取出來的很多好處。解耦讓我們可以把函數用在更多地方以及不同類型的對象上,同時讓它可以與其它函數組合。我們還消除掉了所有對 this 上下文的引用,光這一項對我來說就足夠了!

 

 

 

來自:http://www.w3ctech.com/topic/2001

 

 本文由用戶 dyydyy999 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!