原文链接: http://jrsinclair.com/articles/2016/gentle-introduction-to-functional-javascript-intro/ ;
原文作者: James Sinclair ;
这篇文章是介绍函数式编程的四篇文章中的第一篇。在这篇文章中,我们来看一下让 JavaScript 成为适合函数式编程的组成部分,并且看看为什么它将会是很有用的。
为什么函数式 JavaScript 如此夸大其词呢?为什么它叫做函数式?它不是像任何人去写一个功能失调或者没有函数式的 JavaScript。它的好处是什么?你的烦恼是什么?
对于我来讲,学习函数式编程就像得到了一个 多功能食品料理机 一样:
在我们去思考这为什么是个好主意之前,让我们用 JavaScript 的一些基本特性去实现函数式编程。在 JavaScript 中,有两个关键的组成部分:变量和函数,变量就像一个容器,我们可以把一些东西放在里面,就像这样:
var myContainer = "Hey everybody! Come see how good I look!";
上面的代码创建的一个容器,并且将一个字符串放在了里面。
在另一方面,函数,它是一种途径可以绑定一些指令用于我们可以再使用它,它还能让事情变得有条理,因此我们不用一下子去考虑每一件事情。我们可以创建一个函数像这样:
function log(someVariable) {
console.log(someVariable);
return someVariable;
}
我们可以这样调用它:
log(myContainer);
// Hey everybody! Come see how good I look!
当时,如果你之前了解过一些 JavaScript ,就会知道我们还可以像下面这样编写和调用函数:
var log = function(someVariable) {
console.log(someVariable);
return someVariable;
}
log(myContainer);
// Hey everybody! Come see how good I look!
让我们仔细观察,当我们用这种方式定义一个函数的时候,相当于我们创造一个叫做 log
的变量,然后把一个函数赋值给它。事实也确实如此。我们的 log()
函数是一个变量,这意味着我们可以对他做和其他变量一样的事情。
让我们试一试,或许我们可以把一个函数作为一个参数传给另一个函数:
var classyMessage = function() {
return "Stay classy San Diego!";
}
log(classyMessage);
// [Function]
hahahaha,好像没什么有用的惊喜啊,让我们尝试用不同的方式:
var doSomething = function(thing) {
thing();
}
var sayBigDeal = function() {
var message = "I’m kind of a big deal";
log(message);
}
doSomething(sayBigDeal);
// I’m kind of a big deal
这应该不会让你感到非常激动,当时它让那些计算机科学家非常地激动。它能够把函数放在一个变量中,有时候可以这么说“函数是 JavaScript 中的第一类对象”。这意味着处理对待函数和其他的数据类型就像对象或者字符串没什么两样。并且这一个小的特性将会惊人地有用,为了搞明白为什么,我们得谈一谈 DRY 原则。
程序猿喜欢说 DRY 原则 ———— 不要重复你自己,它的意思是说,如果你需要去多次执行同样的任务,把它们绑定在一类可以重复使用的包里面(就像函数),这样的话,如果你想要调整任务设置的话,你只需在一个地方改动就行了——函数。
让我们看看这个例子,我们使用一个轮播库在页面上放三个轮播组件:
var el1 = document.getElementById('main-carousel');
var slider1 = new Carousel(el1, 3000);
slider1.init();
var el2 = document.getElementById('news-carousel');
var slider2 = new Carousel(el2, 5000);
slider2.init();
var el3 = document.getElementById('events-carousel');
var slider3 = new Carousel(el3, 7000);
slider3.init();
上面的代码有些重复,我们想要给页面上的元素初始化一个轮播组件,并且每个都带有一个特定的 ID ,让我们看看如何在函数中去初始化一个轮播组件,然后给每个 ID 调用这个函数。
function initialiseCarousel(id, frequency) {
var el = document.getElementById(id);
var slider = new Carousel(el, frequency);
slider.init();
return slider;
}
initialiseCarousel('main-carousel', 3000);
initialiseCarousel('news-carousel', 5000);
initialiseCarousel('events-carousel', 7000);
这样,代码就非常简洁了并且很好去维护。我们可以遵循下面一个准则:当我们需要对不同的数据做一系列相同的操作的时候,我们可以把这些操作包装在一个函数中。当是如果这些操作也存在一些不同呢?
var unicornEl = document.getElementById('unicorn');
unicornEl.className += ' magic';
spin(unicornEl);
var fairyEl = document.getElementById('fairy');
fairyEl.className += ' magic';
sparkle(fairyEl);
var kittenEl = document.getElementById('kitten');
kittenEl.className += ' magic';
rainbowTrail(kittenEl);
去重构这些代码有些复杂,它确实是一个重复的模式,但是我们给每个元素调用了不同的函数,我们可以第一步先包装 document.getElementById()
的调用和添加 className
的操作到一个函数,这样可以降低一些重复度:
function addMagicClass(id) {
var element = document.getElementById(id);
element.className += ' magic';
return element;
}
var unicornEl = addMagicClass('unicorn');
spin(unicornEl);
var fairyEl = addMagicClass('fairy');
sparkle(fairyEl);
var kittenEl = addMagicClass('kitten');
rainbow(kittenEl);
但是我们怎样让他变得更 DRY 一些呢?如果你记得 JavaScript 可以允许我们把一个函数作为一个参数传给另一个函数:
function addMagic(id, effect) {
var element = document.getElementById(id);
element.className += ' magic';
effect(element);
}
addMagic('unicorn', spin);
addMagic('fairy', sparkle);
addMagic('kitten', rainbow);
这变得更简洁了,并且更易于维护,这种把函数座位参数传递的能力是我们看到了更多的可能性,在下一节我们将会看到如何使用这种能力是数组变得更友好。
未亡待续…
阅读下一节~