JavaScript 函数式编程实践(来自IBM) |
函数式编程简介 复制代码 代码如下: function map(array, func){ var res = []; for ( var i = 0, len = array.length; i < len; i++){ res.push(func(array[i])); } return res; } var mapped = map([1, 3, 5, 7, 8], function (n){ return n = n + 1; }); print(mapped); 运行这段代码,将会打印: 2,4,6,8,9// 对数组 [1,3,5,7,8] 中每一个元素加 1 注意 map 函数的调用,map 的第二个参数为一个函数,这个函数对 map 的第一个参数 ( 数组 ) 中的每一个都有作用,但是对于 map 之外的代码可能没有任何意义,因此,我们无需为其专门定义一个函数,匿名函数已经足够 。 柯里化 柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术 。这句话有点绕口,我们可以通过例子来帮助理解: 清单 2. 柯里化函数 复制代码 代码如下: function adder(num){ return function (x){ return num + x; } } var add5 = adder(5); var add6 = adder(6); print(add5(1)); print(add6(1)); 结果为: 6 7 比较有意思的是:函数 adder 接受一个参数,并返回一个函数,这个返回的函数可以被预期的那样被调用 。变量 add5 保持着 adder(5) 返回的函数,这个函数可以接受一个参数,并返回参数与 5 的和 。 柯里化在 DOM 的回调中非常有用,我们将在下面的小节中看到 。 高阶函数 高阶函数即为对函数的进一步抽象,事实上,我们在匿名函数小节提到的 map 函数即为一种高阶函数,在很多的函数式编程语言中均有此函数 。map(array, func) 的表达式已经表明,将 func 函数作用于 array 中的每一个元素,最终返回一个新的 array,应该注意的是,map 对 array 和 func 的实现是没有任何预先的假设的,因此称之为“高阶”函数: 清单 3. 高阶函数 复制代码 代码如下: function map(array, func){ var res = []; for ( var i = 0, len = array.length; i < len; i++){ res.push(func(array[i])); } return res; } var mapped = map([1, 3, 5, 7, 8], function (n){ return n = n + 1; }); print(mapped); var mapped2 = map(["one", "two", "three", "four"], function (item){ return "("+item+")"; }); print(mapped2); 将会打印如下结果: 2,4,6,8,9 (one),(two),(three),(four)// 为数组中的每个字符串加上括号 mapped 和 mapped2 均调用了 map,但是得到了截然不同的结果,因为 map 的参数本身已经进行了一次抽象,map 函数做的是第二次抽象,高阶的“阶”可以理解为抽象的层次 。 JavaScript 中的函数式编程 JavaScript 是一门被误解甚深的语言,由于早期的 Web 开发中,充满了大量的 copy-paste 代码,因此平时可以见到的 JavaScript 代码质量多半不高,而且 JavaScript 代码总是很飞动的不断闪烁的 gif 广告,限制网页内容的复制等联系在一起的,因此包括 Web 开发者在内的很多人根本不愿意去学习 JavaScript 。 这种情形在 Ajax 复兴时得到了彻底的扭转,Google Map,Gmail 等 Ajax 应用的出现使人们惊叹:原来 JavaScript 还可以做这样的事!很快,大量优秀的 JavaScript/Ajax 框架不断出现,比如 Dojo,Prototype,jQuery,ExtJS 等等 。这些代码在给页面带来绚丽的效果的同时,也让开发者看到函数式语言代码的优雅 。 函数式编程风格 在 JavaScript 中,函数本身为一种特殊对象,属于顶层对象,不依赖于任何其他的对象而存在,因此可以将函数作为传出 / 传入参数,可以存储在变量中,以及一切其他对象可以做的事情 ( 因为函数就是对象 ) 。 JavaScript 被称为有着 C 语法的 LISP,LISP 代码的一个显著的特点是大量的括号以及前置的函数名,比如: 清单 4. LISP 中的加法 (+ 1 3 4 5 6 7) 加号在 LISP 中为一个函数,这条表达式的意思为将加号后边的所有数字加起来,并将值返回,JavaScript 可以定义同样的求和函数: 清单 5. JavaScript 中的求和 复制代码 代码如下: function sum(){ var res = 0; for ( var i = 0, len = arguments.length; i < len; i++){ res += parseInt(arguments[i]); } return res; } print(sum(1,2,3)); print(sum(1,2,3,4,6,7,8)); 运行此段代码,得到如下结果: 6 31 如果要完全模拟函数式编码的风格,我们可以定义一些诸如: 清单 6. 一些简单的函数抽象 复制代码 代码如下: function add(a, b){ return a+b; } function sub(a, b){ return a-b; } function mul(a, b){ return a*b; } function div(a, b){ return a/b; } function rem(a, b){ return a%b; } function inc(x){ return x + 1; } function dec(x){ return x - 1; } function equal(a, b){ return a==b; } function great(a, b){ return a>b; } function less(a, b){ return a<b; } 这样的小函数以及谓词,那样我们写出的代码就更容易被有函数式编程经验的人所接受: |