函数式 JavaScript:将方法从对象中解耦
<p style="text-align:center"><img src="https://simg.open-open.com/show/2ce3467dd8987bd47691f5aa86f0d5da.jpg"></p> <p>在项目中我一直做的一件事情就是把方法从其对象中解耦。 map 、 filter 以及 reduce 并非是全部,但是它们肯定是首先获得自由的。</p> <p>解耦方法可以让方法摆脱父对象所施加的限制,同时在表示代码的方式上给了我们更多的自由。</p> <h3>那么这到底是啥玩意呢?</h3> <p>为简便起见,我们只从 Array 对象中抽取 map 方法。幸运的是 JavaScript 的原型继承让这变得很简单,因为我们想要的功能就在 Array.prototype.map 中。JavaScript 中一个很棒的事情就是我们可以直接调用这个方法,只不过要用 .call 来调用,因为 map 需要一个 this 参数。</p> <p>揉进点柯里化,一行代码就能搞定。</p> <pre> <code class="language-javascript">const map = f => x => Array.prototype.map.call(x, f)</code></pre> <p>现在我们就可以在没有 Array 对象的情况下调用 map 函数了!</p> <h3>调用 map 的替代方式</h3> <p>调用 map 的方式有很多种,由于 V8 引擎的优化,这些调用方式在性能上其实没有多大差别。</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/b0a3f0050bc9c8a242c36b0cf8285327.gif"></p> <p>有点性能上的差异没多大意义,而且这些数字每次都会变。结论应该是这些方法是差不多的。</p> <h3>解耦如何让我的生活变得更好?</h3> <p>这个问题很好!可以说是最好的问题。我认为这最好用代码来解释,而不是空谈,所以下面我们就直接用代码开始好了。</p> <p>document.querySelectorAll (以及类似的方法)不返回数组,而是返回一个 NodeList 对象,而 NodeList 对象是不包含 map 方法的。虽然你可以采用一些魔法手段,将 NodeList 转换为 Array,但是这种转换是没有必要的。因为 map 可以遍历 NodeList,就好像它是一个数组一样。</p> <pre> <code class="language-javascript">const items = document.querySelectorAll('div') items.map(doSomething) // => Uncaught TypeError: items.map is not a function map(doSomething)(items) // => [<div/>, ..., <div/>]</code></pre> <p>我们甚至可以 map 一个字符串,而不需要先把它转型为字符数组。</p> <pre> <code class="language-javascript">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']</code></pre> <p>解耦让我们可以轻松将一个对象映射转换为一个列表映射:</p> <pre> <code class="language-javascript">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']</code></pre> <p>我们甚至可以对对象进行 map :</p> <pre> <code class="language-javascript">const obj = { 0: 4, 1: 5, 2: 6, length: 3 } map(increase)(obj) // => [5, 6, 7]</code></pre> <p>解耦允许我们组合函数:</p> <pre> <code class="language-javascript">const mapDoStuff = map(doStuff) const mapDoSomething = map(doSomething) // 组合 2 个映射 const mapDoSomethingThenStuff = compose(mapDoStuff, mapDoSomething)</code></pre> <p>解耦(带柯里化)允许我们偏应用函数参数,创建新函数。</p> <pre> <code class="language-javascript">const increaseOne = x => x + 1 // partially applied map increase const increaseMany = map(increaseOne) increaseMany([1, 2, 3]) // => [2, 3, 4]</code></pre> <p>和 this 说再见!!!</p> <pre> <code class="language-javascript">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</code></pre> <p>在本例中, cat.speak 运行正常,但是 catSpeak 不行,因为 this 上下文改变了。这太烦了!如果我们把 speak 方法解耦出来,就 <strong> 再也不用操心 this 了 </strong> !</p> <pre> <code class="language-javascript">const cat = { sound: 'meow' } const speak = ({ sound }) => console.log(sound) speak(cat) // => 'meow'</code></pre> <p>然后我们就可以创建使用解耦过的函数的新函数。</p> <pre> <code class="language-javascript">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!'</code></pre> <h3>总结</h3> <p>本文学习了解耦方法并将其从对象中抽取出来的很多好处。解耦让我们可以把函数用在更多地方以及不同类型的对象上,同时让它可以与其它函数组合。我们还消除掉了所有对 this 上下文的引用,光这一项对我来说就足够了!</p> <p> </p> <p> </p> <p> </p> <p>来自:http://www.w3ctech.com/topic/2001</p> <p> </p>
本文由用户 dyydyy999 自行上传分享,仅供网友学习交流。所有权归原作者,若您的权利被侵害,请联系管理员。
转载本站原创文章,请注明出处,并保留原始链接、图片水印。
本站是一个以用户分享为主的开源技术平台,欢迎各类分享!