JavaScript 函数式编程实践(来自IBM)第2/3页
更新时间:2010年06月29日 08:46:44 作者:
说到函数式编程,人们的第一印象往往是其学院派,晦涩难懂,大概只有那些蓬头散发,不修边幅,甚至有些神经质的大学教授们才会用的编程方式。
清单 7. 函数式编程风格
复制代码 代码如下:
// 修改之前的代码
function factorial(n){
if (n == 1){
return 1;
} else {
return factorial(n - 1) * n;
}
}
// 更接近“函数式”编程风格的代码
function factorial(n){
if (equal(n, 1)){
return 1;
} else {
return mul(n, factorial(dec(n)));
}
}
闭包及其使用
闭包是一个很有趣的主题,当在一个函数 outter 内部定义另一个函数 inner,而 inner 又引用了 outter 作用域内的变量,在 outter 之外使用 inner 函数,则形成了闭包。描述起来虽然比较复杂,在实际编程中却经常无意的使用了闭包特性。
清单 8. 一个闭包的例子
复制代码 代码如下:
function outter(){
var n = 0;
return
function (){
return n++;
}
}
var o1 = outter();
o1();//n == 0
o1();//n == 1
o1();//n == 2
var o2 = outter();
o2();//n == 0
o2();//n == 1
匿名函数 function(){return n++;} 中包含对 outter 的局部变量 n 的引用,因此当 outter 返回时,n 的值被保留 ( 不会被垃圾回收机制回收 ),持续调用 o1(),将会改变 n 的值。而 o2 的值并不会随着 o1() 被调用而改变,第一次调用 o2 会得到 n==0 的结果,用面向对象的术语来说,就是 o1 和 o2 为不同的 实例,互不干涉。
总的来说,闭包很简单,不是吗?但是,闭包可以带来很多好处,比如我们在 Web 开发中经常用到的:
清单 9. jQuery 中的闭包
复制代码 代码如下:
var con = $("div#con");
setTimeout( function (){
con.css({background:"gray"});
}, 2000);
上边的代码使用了 jQuery 的选择器,找到 id 为 con 的 div 元素,注册计时器,当两秒中之后,将该 div 的背景色设置为灰色。这个代码片段的神奇之处在于,在调用了 setTimeout 函数之后,con 依旧被保持在函数内部,当两秒钟之后,id 为 con 的 div 元素的背景色确实得到了改变。应该注意的是,setTimeout 在调用之后已经返回了,但是 con 没有被释放,这是因为 con 引用了全局作用域里的变量 con。
使用闭包可以使我们的代码更加简洁,关于闭包的更详细论述可以在参考信息中找到。由于闭包的特殊性,在使用闭包时一定要小心,我们再来看一个容易令人困惑的例子:
清单 10. 错误的使用闭包
复制代码 代码如下:
var outter = [];
function clouseTest () {
var array = ["one", "two", "three", "four"];
for ( var i = 0; i < array.length;i++){
var x = {};
x.no = i;
x.text = array[i];
x.invoke = function (){
print(i);
}
outter.push(x);
}
}
上边的代码片段很简单,将多个这样的 JavaScript 对象存入 outter 数组:
清单 11. 匿名对象
复制代码 代码如下:
{
no : Number,
text : String,
invoke : function (){
// 打印自己的 no 字段
}
}
我们来运行这段代码:
清单 12. 错误的结果
复制代码 代码如下:
clouseTest();// 调用这个函数,向 outter 数组中添加对象
for ( var i = 0, len = outter.length; i < len; i++){
outter[i].invoke();
}
出乎意料的是,这段代码将打印:
4
4
4
4
而不是 1,2,3,4 这样的序列。让我们来看看发生了什么事,每一个内部变量 x 都填写了自己的 no,text,invoke 字段,但是 invoke 却总是打印最后一个 i。原来,我们为 invoke 注册的函数为:
清单 13. 错误的原因
复制代码 代码如下:
function invoke(){
print(i);
}
每一个 invoke 均是如此,当调用 outter[i].invoke 时,i 的值才会被去到,由于 i 是闭包中的局部变量,for 循环最后退出时的值为 4,因此调用 outter 中的每个元素都会得到 4。因此,我们需要对这个函数进行一些改造:
清单 14. 正确的使用闭包
复制代码 代码如下:
var outter = [];
function clouseTest2(){
var array = ["one", "two", "three", "four"];
for ( var i = 0; i < array.length;i++){
var x = {};
x.no = i;
x.text = array[i];
x.invoke = function (no){
return
function (){
print(no);
}
}(i);
outter.push(x);
}
}
通过将函数 柯里化,我们这次为 outter 的每个元素注册的其实是这样的函数:
复制代码 代码如下:
//x == 0
x.invoke = function (){print(0);}
//x == 1
x.invoke = function (){print(1);}
//x == 2
x.invoke = function (){print(2);}
//x == 3
x.invoke = function (){print(3);}
这样,就可以得到正确的结果了。
实际应用中的例子
好了,理论知识已经够多了,我们下面来看看现实世界中的 JavaScript 函数式编程。有很多人为使 JavaScript 具有面向对象风格而做出了很多努力 (JavaScript 本身具有 可编程性),事实上,面向对象并非必须,使用函数式编程或者两者混合使用可以使代码更加优美,简洁。
jQuery 是一个非常优秀 JavaScript/Ajax 框架,小巧,灵活,具有插件机制,事实上,jQuery 的插件非常丰富,从表达验证,客户端图像处理,UI,动画等等。而 jQuery 最大的特点正如其宣称的那样,改变了人们编写 JavaScript 代码的风格。
优雅的 jQuery
有经验的前端开发工程师会发现,平时做的最多的工作有一定的模式:选择一些 DOM 元素,然后将一些规则作用在这些元素上,比如修改样式表,注册事件处理器等。因此 jQuery 实现了完美的 CSS 选择器,并提供跨浏览器的支持:
您可能感兴趣的文章:
- JavaScript继承基础讲解(原型链、借用构造函数、混合模式、原型式继承、寄生式继承、寄生组合式继承)
- 浅谈JS继承_借用构造函数 & 组合式继承
- javascript组合使用构造函数模式和原型模式实例
- JS继承之借用构造函数继承和组合继承
- JavaScript函数式编程(Functional Programming)箭头函数(Arrow functions)用法分析
- JavaScript函数式编程(Functional Programming)高阶函数(Higher order functions)用法分析
- JavaScript函数式编程(Functional Programming)纯函数用法分析
- JavaScript函数式编程(Functional Programming)声明式与命令式实例分析
- 详解用函数式编程对JavaScript进行断舍离
- 用函数式编程技术编写优美的 JavaScript_ibm
- JavaScript函数式编程(Functional Programming)组合函数(Composition)用法分析
相关文章
javaScript让文本框内的最后一个文字的后面获得焦点实现代码
让文本框内的最后一个文字的后面获得焦点,在应用中很常见,接下来提供解决方案,按兴趣的朋友可以了解下2013-01-01
KnockoutJS 3.X API 第四章之表单submit、enable、disable绑定
Knockout是一个以数据模型(data model)为基础的能够帮助你创建富文本,响应显示和编辑用户界面的JavaScript类库。这篇文章介绍了KnockoutJS 3.X API 第四章之表单submit、enable、disable绑定的相关知识,感兴趣的朋友一起看看吧2016-10-10
Javascript类定义语法,私有成员、受保护成员、静态成员等介绍
JS只是一门支持面向对象编程的语言,通过OO可以让我们的代码组织更加人性化。可是与传统基与类的面向对编程语言不同它没有类概念并且没成员访问修饰符。这多少会给我们编程工作会带来一些束缚2011-12-12
ES6中的迭代器、Generator函数及Generator函数的异步操作方法
这篇文章主要介绍了ES6中的迭代器、Generator函数以及Generator函数的异步操作方法,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下2019-05-05


最新评论