JavaScript 函数式编程理解笔记 (2)
2017-04-11 20:00
711 查看
本笔记经过学习
https://www.ibm.com/developerworks/cn/web/1006_qiujt_jsfunctional/ 后书写,很大部分都相同,但一字一句包括代码都是自己书写。
[b]清单1.JavaScript中的求和[/b]
运行此段代码,结果如下:
如果要完全模拟函数式编码的风格,我们可以定义一些诸如以下的小函数以及谓词:
[b]清单2.一些简单的函数抽象[/b]
[b]清单3.函数式编程风格[/b]
[b]清单4.一个闭包的例子[/b]
匿名函数 function(){return n++;}中包含对outter局部变量n的引用,因此 当outter返回时,会保留n的值(不会被垃圾收集器回收),持续调用o1会改变n的值。而o2的值不会随着o1的调用而改变,是因为o1和o2是不同的实例。
[b]清单5.jQuery中的闭包[/b]
上面的代码使用了jQuery的选择器,找到id为con的div元素,注册计时器,两秒后将此元素的背景设置为灰色。神奇之处在于,在调用了setTimeout函数之后,con依旧被保留在了函数中,两秒之后,这个元素的背景色的确改变了。应注意的是,setTimeout在调用之后已经返回了,但con没有被释放,因为con引用了全局作用域里的变量con。
由于闭包的特殊性,要小心使用闭包,来看一个容易令人困惑的例子:
[b]清单6.错误的使用闭包[/b]
[b]清单7.错误的结果[/b]
出乎意料的是,将打印:
而不是1,2,3,4这样的序列。每一个x都有自己的no,text,invoke字段,但是invoke却打印了最后一个i的值。错误的原因在于注册的函数:
[b]清单8.错误的原因[/b]
每一个invoke都是如此,只有当outter[i].invoke被调用的时候,i的值才会被取到,而i由于是局部变量,for循环退出时i的值总是为4,所以每次打印的结果都会是4。因此,需要对这个函数进行更改:
[b]清单9.正确的用法[/b]
通过将函数柯里化,可以达到我们预期的效果,即打印出1,2,3,4这样的序列。
jQuery实现了完美的CSS选择器,并提供跨浏览器的支持:
[b]清单10.jQuery选择器[/b]
jQuery的选择器规则很丰富。这里需要注意的是,jQuery选择出来的jQuery对象本质上是一个List。
有了List,我们可以这么操作:
[b]清单11.jQuery操作jQuery对象(List)[/b]
同时,我们也可以扩大或缩小这个List:
[b]清单12.扩大/缩小jQuery集合[/b]
现在有一个小例子,假设有这样一个页面:
[b]清单13.页面的HTML结构[/b]
效果图如下:
[b]图1.过滤之前的效果[/b]
我们通过jQuery对这个集合做一个过滤,在这个例子中,我们保留这样的div,当且仅当这个div中包含一个类名为title的span,而且span中的内容是数字:
[b]清单14.过滤集合[/b]
效果图如下:
[b]图2.过滤之后的效果[/b]
再来看看jQuery中对数组的操作:
[b]清单15.jQuery对数组的函数式操作[/b]
mapped将会变为[2,3,4,5,6,7,8,9,10,11],而greped会变为[2,4,6,8,10]。
最后看一个更接近实际的例子:
[b]清单16.一个页面刷新的例子[/b]
首先声明一个柯里化的函数 update,这个函数会将传入的参数作为选择器的 id,并更新这个 div 的内容 (innerHTML)。然后声明一个函数 refresh,refresh 接受两个参数,第一个参数为服务器端的 url,第二个参数为一个回调函数,当服务器端成功返回时,调用该函数。
这种模式在实际的编程中相当有效,因为关于如何与服务器通信,以及如果选取页面内容的部分被很好的抽象成函数,现在我们需要做的就是将 url 和 id 传递给 refresh,即可完成需要的动作。函数式编程在很大程度上降低了这个过程的复杂性,这正是我们选择使用该思想的最终原因。
https://www.ibm.com/developerworks/cn/web/1006_qiujt_jsfunctional/ 后书写,很大部分都相同,但一字一句包括代码都是自己书写。
JavaScript 函数式编程理解笔记(2)
JavaScript中的函数式编程
函数式编程风格
在JavaScript中,函数本身做为一种特殊对象,属于顶层对象,不依赖于其他对象存在。[b]清单1.JavaScript中的求和[/b]
function sum(){ var res = 0; for (var i = 0; i < arguments.length; i++){ res += parseInt(argument[i]); } return res; } print(sum(1,2,3)); print(sum(1,2,3,4,5,6));
运行此段代码,结果如下:
6 31
如果要完全模拟函数式编码的风格,我们可以定义一些诸如以下的小函数以及谓词:
[b]清单2.一些简单的函数抽象[/b]
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; }
[b]清单3.函数式编程风格[/b]
//修改之前的代码 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函数,则形成了闭包。[b]清单4.一个闭包的例子[/b]
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的调用而改变,是因为o1和o2是不同的实例。
[b]清单5.jQuery中的闭包[/b]
var con = $("div#con"); setTimeout(function(){ con.css({background:"gray"}); },2000);
上面的代码使用了jQuery的选择器,找到id为con的div元素,注册计时器,两秒后将此元素的背景设置为灰色。神奇之处在于,在调用了setTimeout函数之后,con依旧被保留在了函数中,两秒之后,这个元素的背景色的确改变了。应注意的是,setTimeout在调用之后已经返回了,但con没有被释放,因为con引用了全局作用域里的变量con。
由于闭包的特殊性,要小心使用闭包,来看一个容易令人困惑的例子:
[b]清单6.错误的使用闭包[/b]
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); } }
[b]清单7.错误的结果[/b]
clouseText(); //调用这个函数,向outter数组中添加对象 for (var i = 0; i < outter.length; i++){ outter[i].invoke(); }
出乎意料的是,将打印:
4 4 4 4
而不是1,2,3,4这样的序列。每一个x都有自己的no,text,invoke字段,但是invoke却打印了最后一个i的值。错误的原因在于注册的函数:
[b]清单8.错误的原因[/b]
function invoke(){ print(i); }
每一个invoke都是如此,只有当outter[i].invoke被调用的时候,i的值才会被取到,而i由于是局部变量,for循环退出时i的值总是为4,所以每次打印的结果都会是4。因此,需要对这个函数进行更改:
[b]清单9.正确的用法[/b]
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); } }
通过将函数柯里化,可以达到我们预期的效果,即打印出1,2,3,4这样的序列。
实际应用中的例子
[b]优雅的jQuery[/b]jQuery实现了完美的CSS选择器,并提供跨浏览器的支持:
[b]清单10.jQuery选择器[/b]
var cons = $("div.note");// 找出所有具有 note 类的 div var con = $("div#con"); // 找出 id 为 con 的 div 元素 var links = $("a"); // 找出页面上所有的链接元素
jQuery的选择器规则很丰富。这里需要注意的是,jQuery选择出来的jQuery对象本质上是一个List。
有了List,我们可以这么操作:
[b]清单11.jQuery操作jQuery对象(List)[/b]
cons.each( function (index){ $( this ).click( function (){ //do something with the node }); });
同时,我们也可以扩大或缩小这个List:
[b]清单12.扩大/缩小jQuery集合[/b]
cons.find("span.title");// 在 div.note 中进行更细的筛选 cons.add("div.warn"); // 将 div.note 和 div.warn 合并起来 cons.slice(0, 5); // 获取 cons 的一个子集
现在有一个小例子,假设有这样一个页面:
[b]清单13.页面的HTML结构[/b]
<div class="note"> <span class="title">Hello, world</span> </div> <div class="note"> <span class="title">345</span> </div> <div class="note"> <span class="title">Hello, world</span> </div> <div class="note"> <span class="title">67</span> </div> <div class="note"> <span class="title">483</span> </div>
效果图如下:
[b]图1.过滤之前的效果[/b]
我们通过jQuery对这个集合做一个过滤,在这个例子中,我们保留这样的div,当且仅当这个div中包含一个类名为title的span,而且span中的内容是数字:
[b]清单14.过滤集合[/b]
var cons = $("div.note").hide(); //选择note类的div,并隐藏 cons.filter(function (){ return $(this).find("span.title").html().match(/^\d+$/); }).show();
效果图如下:
[b]图2.过滤之后的效果[/b]
再来看看jQuery中对数组的操作:
[b]清单15.jQuery对数组的函数式操作[/b]
var mapped = $.map([1,2,3,4,5,6,7,8,9,10], function (n){ return n + 1; }); var greped = $.grep([1,2,3,4,5,6,7,8,9,10], function (n){ return n % 2 ==0; });
mapped将会变为[2,3,4,5,6,7,8,9,10,11],而greped会变为[2,4,6,8,10]。
最后看一个更接近实际的例子:
[b]清单16.一个页面刷新的例子[/b]
function update (item){ return function (text){ $("div#"+item).html(text); } } function refresh (url, callback){ var params = { type: "echo", data: "" }; $.ajax({ type: "post", url: url, cache: false, async: true, dataType: "json", data: params, success: function (data, status){ callback(data); }, error: function (err){ alert("error:" + err); } }); }
首先声明一个柯里化的函数 update,这个函数会将传入的参数作为选择器的 id,并更新这个 div 的内容 (innerHTML)。然后声明一个函数 refresh,refresh 接受两个参数,第一个参数为服务器端的 url,第二个参数为一个回调函数,当服务器端成功返回时,调用该函数。
这种模式在实际的编程中相当有效,因为关于如何与服务器通信,以及如果选取页面内容的部分被很好的抽象成函数,现在我们需要做的就是将 url 和 id 传递给 refresh,即可完成需要的动作。函数式编程在很大程度上降低了这个过程的复杂性,这正是我们选择使用该思想的最终原因。
相关文章推荐
- JavaScript 函数式编程理解笔记 (1)
- [学习笔记]JavaScript之函数式编程
- JavaScript 学习笔记十二 函数式编程风格
- Javascript编程笔记一:理解变量和函数提前声明
- JavaScript 学习笔记十二 函数式编程风格
- 比较不错的函数式JavaScript编程指南教程
- 用函数式编程技术编写优美的 JavaScript_ibm
- JavaScript与函数式编程解释
- javascript 函数式编程
- 函数式JavaScript编程指南
- 转贴:用函数式编程技术编写优美的 JavaScript
- 用函数式编程技术编写优美的 JavaScript
- 用函数式编程技术编写优美的 JavaScript
- JavaScript 函数式编程的原理
- JavaScript的学习笔记(3)对象化编程
- Javascript高级编程所要理解的
- 用函数式编程技术编写优美的 JavaScript_ibm
- JavaScript与函数式编程解释
- javascript 之趣味 函数式编程
- JavaScript高级编程笔记(1)