本文转载自:众成翻译
译者:网络埋伏纪事
链接:http://www.zcfy.cc/article/2643
原文:https://hackernoon.com/functional-javascript-decoupling-methods-from-their-objects-aa3ca13d7ae8
在项目中我一直做的一件事情就是把方法从其对象中解耦。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
的方式有很多种,由于 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
上下文的引用,光这一项对我来说就足够了!
扫码关注w3ctech微信公众号
共收到0条回复