自己写的javascript map 排序数组排序函数,虽然效果实现了,但结果不是我预想的那样,这是为什么?

Javascript中数组如何排序
在Javascript中我们已知有两个可以直接用来进行数组排序的方法reverse()和sort()。其中reverse()是按照反向对于数组进行排序的,而sort()是按照正向进行排序的。举个例子:&
var values = [ 1, 2, 3, 4,
items=values.reverse();&
document.write(items); // 5, 4,
现在数组的顺序是5,4,3,2,1。这里很方便。&
再看一下sort()数组。&
var values=[ 0, 1, 5, 10,
items=values.sort();&
document.write(items); // 0, 1,
10 ,15, 5&
这里,细心的同学可能已经发现了问题,这里的数组排序方法并不是按照我们想像中的数字大小来排序的,而是按照字符串测试结果改变原先的数据,字符串"15"位于"5"的前面,所以被排到了最后一个。这并不是我们想要的。&
那么如何才可以得到我们想要的按照我们思维中的数字大小来排序呢。我们可以自己编写一个函数来实现。&
compare(value1,value2)&
if (value1 &
else if (value1 &
这时我们将这个函数与sort()函数集合起来使用:&
values=[0,1,5,10,15];&
items=values.sort(compare);&
document.write(items); //
测试结果:0,1,5,10,15&
这种方法可以使得数组保持了正确的顺序输出。当然也可以通过比较函数获得数组的反向排序,代码如下:&
compare(value1,value2){&
if (value1 &
else if(value1 &
这样之后,数组就会以15,10,5,1,0的正确排序输出了。
eiya.me&严颖的个人博客
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。本帖子已过去太久远了,不再提供回复功能。有趣的原生javascript数组函数
有趣的原生javascript数组函数
发布时间: 19:42:07
编辑:www.fx114.net
本篇文章主要介绍了"有趣的原生javascript数组函数",主要涉及到有趣的原生javascript数组函数方面的内容,对于有趣的原生javascript数组函数感兴趣的同学可以参考一下。
在JavaScript中,可以通过两种方式创建数组,构造函数和数组直接量, 其中后者为首选方法。数组对象继承自Object.prototype,对数组执行typeof操作符返回‘object’而不是‘array’。然而执行[]
instanceof Array返回true。此外,还有类数组对象是问题更复杂,如字符串对象,arguments对象。arguments对象不是Array的实例,但却有个length属性,并且值能通过索引获取,所以能像数组一样通过循环操作。
在本文中,我将复习一些数组原型的方法,并探索这些方法的用法。
循环.forEach断言.some和.every.join和.concat的区别栈和队列.pop,.push,.shift和.unshift模型映射.map查询.filter排序.sort计算.reduce和.reduceRight复制.slice万能的.splice查找.indexOfin操作符走进.reverse
如果你想测试上面的例子,您可以复制并粘贴到您的浏览器的控制台中。
循环.forEach
这是JavaScript原生数组方法中最简单的方法。不用怀疑,IE7和IE8不支持此方法。
forEach方法需要一个回调函数,数组内的每个元素都会调用一次此方法,此方法需要三个参数如下:
value&当前操作的数组元素当前操作元素的数组索引array&当前数组的引用
此外,可以传递可选的第二个参数,作为每个调用函数的上下文(this)。
['_', 't', 'a', 'n', 'i', 'f', ']'].forEach(function (value, index, array) {
this.push(String.fromCharCode(value.charCodeAt() + index + 2))
}, out = [])
out.join('')
// &- 'awesome'
.join函数我将在下文提及,上面例子中,它将数组中的不同元素拼接在一起,类似于如下的效果:out[0]
+ '' + out[1] + '' + out[2] + '' + out[n]。
我们不能用break中断forEach循环,抛出异常是不明智的方法。幸运的是,我们有其他的方法中断操作。
断言.some和.every
如果你曾经用过.NET的枚举,这些方法的名字和.Any(x =& x.IsAwesome)&和&.All(x
=& x.IsAwesome)非常相似。
这些方法和.forEach类似,需要一个包含value,index,和array三个参数的回调函数,并且也有一个可选的第二个上下文参数。MDN对.some的描述如下:
some将会给数组里的每一个元素执行一遍回调函数,直到有一个回调函数返回true位置。如果找到目标元素,some立即返回true,否则some返回false。回调函数只对已经指定值的数组索引执行;它不会对已删除的或未指定值的元素执行。
max = -Infinity
satisfied = [10, 12, 10, 8, 5, 23].some(function (value, index, array) {
if (value & max) max = value
return value & 10
console.log(max)
// &- true
注意,当回调函数的value & 10&条件满足时,中断函数循环。.every的工作行为类似,但回调函数要返回false而不是true。
.join和.concat的区别
.join方法经常和.concat混淆。.join(分隔符)方法创建一个字符串,会将数组里面每个元素用分隔符连接。如果没有提供分隔符,默认的分隔符为“,”。.concat方法创建一个新数组,其是对原数组的浅拷贝(注意是浅拷贝哦)。
.concat&的标志用法:array.concat(val,
val2, val3, valn).concat&返回一个新书组array.concat()没有参数的情况下,会返回原数组的浅拷贝
浅拷贝意味着新数组和原数组保持相同的对象引用,这通常是好事。例如:
var a = { foo: ‘bar’ } var b = [1, 2, 3, a] var c = b.concat()
console.log(b === c) // &- false
b[3] === a && c[3] === a // &- true
栈和队列.pop,.push,.shift和.unshift
每个人都知道向数组添加元素用.push。但你知道一次可以添加多个元素吗?如下[].push('a',
'b', 'c', 'd', 'z')。
.pop方法和.push成对使用,它返回数组的末尾元素并将元素从数组移除。如果数组为空,返回void
0(undefined)。使用.push和.pop我们能轻易模拟出LIFO(后进先出或先进后出)栈。
function Stack () {
this._stack = []
Stack.prototype.next = function () {
return this._stack.pop()
Stack.prototype.add = function () {
return this._stack.push.apply(this._stack, arguments)
stack = new Stack()
stack.add(1,2,3)
stack.next()
相反,我们可以用.unshift&和&.shift模拟FIFO(先进先出)队列。
function Queue () {
this._queue = []
Queue.prototype.next = function () {
return this._queue.shift()
Queue.prototype.add = function () {
return this._queue.unshift.apply(this._queue, arguments)
queue = new Queue()
queue.add(1,2,3)
queue.next()
用.shift或.pop能很容易遍历数组元素,并做一些操作。
list = [1,2,3,4,5,6,7,8,9,10]
while (item = list.shift()) {
console.log(item)
模型映射.map
map&方法会给原数组中的每个元素(必须有值)都调用一次&callback&函数.callback&每次执行后的返回值组合起来形成一个新数组。callback函数只会在有值的索引上被调用;
那些从来没被赋过值或者使用delete删除的索引则不会被调用。——MDN
Array.prototype.map方法和上面我们提到的.forEach,.some和.every有相同的参数:.map(fn(value,
index, array), thisArgument)。
values = [void 0, null, false, '']
values[7] = void 0
result = values.map(function(value, index, array){
console.log(value)
return value
// &- [undefined, null, false, '', undefined × 3, undefined]
undefined × 3&值解释.map不会在没被赋过值或者使用delete删除的索引上调用,但他们仍然被包含在结果数组中。map在遍历或改变数组方面非常有用,如下所示:
[1, '2', '30', '9'].map(function (value) {
return parseInt(value, 10)
// 1, 2, 30, 9
[97, 119, 101, 115, 111, 109, 101].map(String.fromCharCode).join('')
// &- 'awesome'
// 一个映射新对象的通用模式
items.map(function (item) {
id: item.id,
name: computeName(item)
查询.filter
filter对每个数组元素执行一次回调函数,并返回一个由回调函数返回true的元素
组成的新数组。回调函数只会对已经指定值的数组项调用。
用法例子:.filter(fn(value, index, array), thisArgument)。把它想象成.Where(x
=& x.IsAwesome)&LINQ expression(如果你熟悉C#),或者SQL语句里面的WHERE。考虑到.filter仅返回callback函数返回真值的值,下面是一些有趣的例子。没有传递给回调函数测试的元素被简单的跳过,不会包含进返回的新书组里。
[void 0, null, false, '', 1].filter(function (value) {
return value
[void 0, null, false, '', 1].filter(function (value) {
return !value
// &- [void 0, null, false, '']
排序.sort(比较函数)
如果未提供比较函数,元素会转换为字符串,并按字典许排列。例如,在字典序里,“80”排在“9”之前,但实际上我们希望的是80在9之后(数字排序)。
像大部分排序函数一样,Array.prototype.sort(fn(a,b))需要一个包含两个测试参数的回调函数,并且要产生一下三种返回值之一:
如果a在b前,则返回值小于零如果a和b是等价的,则返回值等于零如果a在b后,则返回值大于零
[9,80,3,10,5,6].sort()
// &- [10, 3, 5, 6, 80, 9]
[9,80,3,10,5,6].sort(function (a, b) {
return a - b
// &- [3, 5, 6, 9, 10, 80]
计算.reduce和.reduceRight
首先reduce函数不是很好理解,.reduce从左到右而.reduceRight从右到左循环遍历数组,每次调用接收目前为止的部分结果和当前遍历的值。
两种方法都有如下典型用法:.reduce(callback(previousValue,
currentValue, index, array), initialValue)。
previousValue是最后被调用的回调函数的返回值,initialValue是开始时previousValue被初始化的值。currentValue&是当前被遍历的元素值,index是当前元素在数组中的索引值。array是对调用.reduce数组的简单引用。
一个典型的用例,使用.reduce的求和函数。
Array.prototype.sum = function () {
return this.reduce(function (partial, value) {
return partial + value
[3,4,5,6,10].sum()
上面提到如果想把数组连成一个字符串,可以使用.join。当数组的值是对象的情况下,除非对象有能返回其合理值的valueof或toString方法,否则.join的表现和你期望的不一样。然而,我们可以使用.reduce作为对象的字符串生成器。
function concat (input) {
return input.reduce(function (partial, value) {
if (partial) {
partial += ', '
return partial + value
{ name: 'George' },
{ name: 'Sam' },
{ name: 'Pear' }
// &- 'George, Sam, Pear'
复制.slice
和.concat类似,调用.slice缺省参数时,返回原数组的浅拷贝。slice函数需要两个参数,一个是开始位置和一个结束位置。
Array.prototype.slice能被用来将类数组对象转换为真正的数组。
Array.prototype.slice.call({ 0: 'a', 1: 'b', length: 2 })
// &- ['a', 'b']
这对.concat不适用,因为它会用数组包裹类数组对象。
Array.prototype.concat.call({ 0: 'a', 1: 'b', length: 2 })
// &- [{ 0: 'a', 1: 'b', length: 2 }]
除此之外,另一个常见用途是从参数列表中移除最初的几个元素,并将类数组对象转换为真正的数组。
function format (text, bold) {
if (bold) {
text = '&b&' + text + '&/b&'
var values = Array.prototype.slice.call(arguments, 2)
values.forEach(function (value) {
text = text.replace('%s', value)
return text
format('some%sthing%s %s', true, 'some', 'other', 'things')
// &- &b&somesomethingother things&/b&
万能的.splice
.splice是我最喜欢的原生数组函数之一。它允许你删除元素,插入新元素,或在同一位置同时进行上述操作,而只使用一个函数调用。注意和.concat和.slice不同的是.splice函数修改原数组。
var source = [1,2,3,8,8,8,8,8,9,10,11,12,13]
var spliced = source.splice(3, 4, 4, 5, 6, 7)
console.log(source)
// &- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ,13]
// &- [8, 8, 8, 8]
你可能已经注意到,它也返回被删除的元素。如果你想遍历已经删除的数组时这可能会派上用场。
var source = [1,2,3,8,8,8,8,8,9,10,11,12,13]
var spliced = source.splice(9)
spliced.forEach(function (value) {
console.log('removed', value)
// &- removed 10
// &- removed 11
// &- removed 12
// &- removed 13
console.log(source)
// &- [1, 2, 3, 8, 8, 8, 8, 8, 9]
查找.indexOf
通过.indexOf,我们可以查找数组元素的位置。如果没有匹配元素则返回-1。我发现我用的很多的一个模式是连续比较,例如a
=== 'a' || a === 'b' || a === 'c',或者即使只有两个结果的比较。在这种情况下,你也可以使用.indexOf,像这样:['a',
'b', 'c'].indexOf(a) !== -1。
注意这对指向同一个引用的对象同样适用。第二个参数是开始查询的起始位置。
var a = { foo: 'bar' }
var b = [a, 2]
console.log(b.indexOf(1))
console.log(b.indexOf({ foo: 'bar' }))
console.log(b.indexOf(a))
console.log(b.indexOf(a, 1))
b.indexOf(2, 1)
如果你想从后向前搜索,.lastIndexOf能派上用场。
在面试中新手容易犯的错误是混淆.indexOf和in操作符,如下:
var a = [1, 2, 5]
// &- true, 但因为 2!
// &- false
问题的关键是in操作符检索对象的键而非值。当然,这在性能上比.indexOf快得多。
var a = [3, 7, 6]
1 in a === !!a[1]
// &- true
in操作符类似于将键值转换为布尔值。!!表达式通常被开发者用来双重取非一个值(转化为布尔值)。实际上相当于强制转换为布尔值,任何为真的值被转为true,任何为假的值被转换为false。
走进.reverse
这方法将数组中的元素翻转并替换原来的元素。
var a = [1, 1, 7, 8]
a.reverse()
// [8, 7, 1, 1]
和复制不同的是,数组本身被更改。在以后的文章中我将展开对这些概念的理解,去看看如何创建一个库,如Underscore或Lo-Dash。
本文出处&/javascript//%E6%9C%89%E8%B6%A3%E7%9A%84JavaScript%E5%8E%9F%E7%94%9F%E6%95%B0%E7%BB%84%E5%87%BD%E6%95%B0/
本文标题:
本页链接:博客访问: 249743
博文数量: 76
博客积分: 2510
博客等级: 少校
技术积分: 825
注册时间:
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: 系统运维
JavaScript的数组排序函数 sort方法,默认是按照ASCII 字符顺序进行升序排列。   arrayobj.sort(sortfunction);   参数:sortFunction   可选项。是用来确定元素顺序的函数的名称。如果这个参数被省略,那么元素将按照 ASCII 字符顺序进行升序排列。   sort 方法将 Array 对象进行适当的排序;在执行过程中并不会创建新的 Array 对象。   如果为 sortfunction 参数提供了一个函数,那么该函数必须返回下列值之一:   负值,如果所传递的第一个参数比第二个参数小。   零,如果两个参数相等。   正值,如果第一个参数比第二个参数大。   以上的方法在一维的排序还是很方便的,但像SQL语句中的ORDER BY 一样的多键值排序由怎么做呢?   多维数组的多键值排序,则需要复杂一些,但不需要用循环解决。实际解决的道理是一样的 。   数字:   以下的例子是将数字的多维数组按照第5列,第9列,第3列的顺序排序,像SQL语句中的ORDER BY col5,col9,col7。数字的时候可以直接两个项目相减,以结果作为返回值即可。
  字符:   字符的时候sortFunction中的项目不能像数字一样直接相减,需要调用   str1.localeCompare( str2 )方法来作比较,从而满足返回值。以下是多维数组的第1,2列作排序的情况。 function sortFunction(array) ...{return array.sort( function(x, y) ...{return (x[0]==y[0])?(x[1].localeCompare(y[1])):(x[0].localeCompare(y[0]))});}  因此arrayObject.sort( sortFunction )的排序功能还是很强大的,终于能够实现了SQL语句中的ORDER BY 一样的功能。
阅读(1236) | 评论(0) | 转发(0) |
相关热门文章
给主人留下些什么吧!~~
请登录后评论。developerWorks 社区
JavaScript 是一门优美的语言,具有动态性,弱类型,并有 C 和 LISP 的双重语法,重要的是,她本身是“可编程”的。文章先对 JavaScript 的函数式编程特性做一些介绍,然后讨论函数式编程在实际项目中的基本应用。
, 软件工程师, Jinfonet
邱俊涛,毕业于昆明理工大学计算机科学与技术专业,对机械控制、电子、人工智能等方面有浓厚的兴趣,对计算机科学的底层比较熟悉。喜欢 C/Java/Python 等语言。
基础知识函数式编程简介说到函数式编程,人们的第一印象往往是其学院派,晦涩难懂,大概只有那些蓬头散发,不修边幅,甚至有些神经质的大学教授们才会用的编程方式。这可能在历史上的某个阶段的确如此,但是近来函数式编程已经在实际应用中发挥着巨大作用了,而更有越来越多的语言不断的加入诸如 闭包,匿名函数等的支持,从某种程度上来讲,函数式编程正在逐步“同化”命令式编程。函数式编程思想的源头可以追溯到 20 世纪 30 年代,数学家阿隆左 . 丘奇在进行一项关于问题的可计算性的研究,也就是后来的 lambda 演算。lambda 演算的本质为 一切皆函数,函数可以作为另外一个函数的输出或者 / 和输入,一系列的函数使用最终会形成一个表达式链,这个表达式链可以最终求得一个值,而这个过程,即为计算的本质。然而,这种思想在当时的硬件基础上很难实现,历史最终选择了同丘奇的 lambda 理论平行的另一种数学理论:图灵机作为计算理论,而采取另一位科学家冯 . 诺依曼的计算机结构,并最终被实现为硬件。由于第一台计算机即为冯 . 诺依曼的程序存储结构,因此运行在此平台的程序也继承了这种基因,程序设计语言如 C/Pascal 等都在一定程度上依赖于此体系。到了 20 世纪 50 年代,一位 MIT 的教授 John McCarthy 在冯 . 诺依曼体系的机器上成功的实现了 lambda 理论,取名为 LISP(LISt Processor), 至此函数式编程语言便开始活跃于计算机科学领域。函数式编程语言特性在函数式编程语言中,函数是第一类的对象,也就是说,函数 不依赖于任何其他的对象而可以独立存在,而在面向对象的语言中,函数 ( 方法 ) 是依附于对象的,属于对象的一部分。这一点 j 决定了函数在函数式语言中的一些特别的性质,比如作为传出 / 传入参数,作为一个普通的变量等。区别于命令式编程语言,函数式编程语言具有一些专用的概念,我们分别进行讨论:匿名函数在函数式编程语言中,函数是可以没有名字的,匿名函数通常表示:“可以完成某件事的一块代码”。这种表达在很多场合是有用的,因为我们有时需要用函数完成某件事,但是这个函数可能只是临时性的,那就没有理由专门为其生成一个顶层的函数对象。比如:清单 1. map 函数
function map(array, func){
var res = [];
for ( var i = 0, len = array. i & i++){
res.push(func(array[i]));
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){
function (x){
return num +
var add5 = adder(5);
var add6 = adder(6);
print(add5(1));
print(add6(1));结果为:67比较有意思的是:函数 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. i & i++){
res.push(func(array[i]));
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. i & i++){
res += parseInt(arguments[i]);
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; }这样的小函数以及谓词,那样我们写出的代码就更容易被有函数式编程经验的人所接受:清单 7. 函数式编程风格 // 修改之前的代码
function factorial(n){
if (n == 1){
return factorial(n - 1) *
// 更接近“函数式”编程风格的代码
function factorial(n){
if (equal(n, 1)){
return mul(n, factorial(dec(n)));
}闭包及其使用闭包是一个很有趣的主题,当在一个函数 outter 内部定义另一个函数 inner,而 inner 又引用了 outter 作用域内的变量,在 outter 之外使用 inner 函数,则形成了闭包。描述起来虽然比较复杂,在实际编程中却经常无意的使用了闭包特性。清单 8. 一个闭包的例子function outter(){
var n = 0;
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.i++){
var x = {};
x.text = array[i];
x.invoke =
function (){
outter.push(x);
}上边的代码片段很简单,将多个这样的 JavaScript 对象存入 outter 数组:清单 11. 匿名对象 {
no : Number,
text : String,
function (){
// 打印自己的 no 字段
}我们来运行这段代码:清单 12. 错误的结果 clouseTest();// 调用这个函数,向 outter 数组中添加对象
for ( var i = 0, len = outter. i & i++){
outter[i].invoke();
出乎意料的是,这段代码将打印:
4而不是 1,2,3,4 这样的序列。让我们来看看发生了什么事,每一个内部变量 x 都填写了自己的 no,text,invoke 字段,但是 invoke 却总是打印最后一个 i。原来,我们为 invoke 注册的函数为:清单 13. 错误的原因function invoke(){
}每一个 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.i++){
var x = {};
x.text = array[i];
x.invoke =
function (no){
function (){
print(no);
outter.push(x);
}通过将函数 柯里化,我们这次为 outter 的每个元素注册的其实是这样的函数: //x == 0
x.invoke =
function (){print(0);}
x.invoke =
function (){print(1);}
x.invoke =
function (){print(2);}
x.invoke =
function (){print(3);}这样,就可以得到正确的结果了。实际应用中的例子好了,理论知识已经够多了,我们下面来看看现实世界中的 JavaScript 函数式编程。有很多人为使 JavaScript 具有面向对象风格而做出了很多努力 (JavaScript 本身具有 可编程性),事实上,面向对象并非必须,使用函数式编程或者两者混合使用可以使代码更加优美,简洁。jQuery 是一个非常优秀 JavaScript/Ajax 框架,小巧,灵活,具有插件机制,事实上,jQuery 的插件非常丰富,从表达验证,客户端图像处理,UI,动画等等。而 jQuery 最大的特点正如其宣称的那样,改变了人们编写 JavaScript 代码的风格。优雅的 jQuery有经验的前端开发工程师会发现,平时做的最多的工作有一定的模式:选择一些 DOM 元素,然后将一些规则作用在这些元素上,比如修改样式表,注册事件处理器等。因此 jQuery 实现了完美的 CSS 选择器,并提供跨浏览器的支持:清单 15. jQuery 选择器
var cons = $("div.note");// 找出所有具有 note 类的 div
var con = $("div#con");// 找出 id 为 con 的 div 元素
var links = $("a");// 找出页面上所有的链接元素当然,jQuery 的选择器规则非常丰富,这里要说的是:用 jQuery 选择器选择出来的 jQuery 对象本质上是一个 List,正如 LISP 语言那样,所有的函数都是基于 List 的。有了这个 List,我们可以做这样的动作:清单 16. jQuery 操作 jQuery 对象 (List) cons.each( function (index){
$( this ).click( function (){
//do something with the node
});想当与对 cons 这个 List中的所有元素使用 map( 还记得我们前面提到的 map 吗? ),操作结果仍然为一个 List。我们可以任意的扩大 / 缩小这个列表,比如:清单 17. 扩大 / 缩小 jQuery 集合 cons.find("span.title");// 在 div.note 中进行更细的筛选
cons.add("div.warn");// 将 div.note 和 div.warn 合并起来
cons.slice(0, 5);// 获取 cons 的一个子集现在我们来看一个小例子,假设有这样一个页面:清单 18. 页面的 HTML 结构 &div class="note"&
&span class="title"&Hello, world&/span&
&div class="note"&
&span class="title"&345&/span&
&div class="note"&
&span class="title"&Hello, world&/span&
&div class="note"&
&span class="title"&67&/span&
&div class="note"&
&span class="title"&483&/span&
&/div&效果如下:图 1. 过滤之前的效果我们通过 jQuery 对包装集进行一次过滤,jQuery 的过滤函数可以使得选择出来的列表对象只保留符合条件的,在这个例子中,我们保留这样的 div,当且仅当这个 div 中包含一个类名为 title 的 span,并且这个 span 的内容为数字:清单 19. 过滤集合 var cons = $("div.note").hide();// 选择 note 类的 div, 并隐藏
cons.filter( function (){
return $( this ).find("span.title").html().match(/^\d+$/);
}).show();效果如下图所示:图 2. 过滤之后的效果我们再来看看 jQuery 中对数组的操作 ( 本质上来讲,JavaScript 中的数组跟 List 是很类似的 ),比如我们在前面的例子中提到的 map 函数,过滤器等:清单 20. jQuery 对数组的函数式操作 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]我们再来看一个更接近实际的例子:清单 21. 一个页面刷新的例子function update(item){
function (text){
$("div#"+item).html(text);
function refresh(url, callback){
var params = {
type : "echo",
type:"post",
cache: false ,
async: true ,
dataType:"json",
data:params,
function (data, status){
callback(data);
function (err){
alert("error : "+err);
refresh("action.do/op=1", update("content1"));
refresh("action.do/op=2", update("content2"));
refresh("action.do/op=3", update("content3"));首先声明一个柯里化的函数 update,这个函数会将传入的参数作为选择器的 id,并更新这个 div 的内容 (innerHTML)。然后声明一个函数 refresh,refresh 接受两个参数,第一个参数为服务器端的 url,第二个参数为一个回调函数,当服务器端成功返回时,调用该函数。然后我们陆续调用三次 refresh,每次的 url 和 id 都不同,这样可以将 content1,content2,conetent3 的内容通过异步方式更新。这种模式在实际的编程中相当有效,因为关于如何与服务器通信,以及如果选取页面内容的部分被很好的抽象成函数,现在我们需要做的就是将 url 和 id 传递给 refresh,即可完成需要的动作。函数式编程在很大程度上降低了这个过程的复杂性,这正是我们选择使用该思想的最终原因。结束语实际的应用中,不会囿于函数式或者面向对象,通常是两者混合使用,事实上,很多主流的面向对象语言都在不断的完善自己,比如加入一些函数式编程语言的特征等,JavaScript 中,这两者得到了良好的结合,代码不但可以非常简单,优美,而且更易于调试。文中仅仅提到 jQuery 特征的一小部分,如果感兴趣,则可以在参考资料中找到更多的链接,jQuery 非常的流行,因此你可以找到很多论述如何使用它的文章。
参考资料 官方网站的地址,可以下载到最新的 jQuery 库。:一篇优秀的关于 JavaScript 闭包的论述。
文中提到的 的译文,该文详细的描述了 LISP 的其中基本原语,很好的解释了 LISP 的 可编程性。:一篇关于 JavaScript 函数式编程的基本概念的文章。“”:在本文中,您将了解如何通过 JavaScript 框架更轻松、更快速地创建具有高度交互性和响应性的 Web 站点和 Web 应用程序。“”:本专题为您收集了一些和目前业界比较流行的 JavaScript 开发工具包相关的资源,从初级的入门介绍到高级的使用以及和其他开发语言、软件集成的内容。developerWorks 和:随时关注 developerWorks 技术活动和网络广播。
:通过专门关于 Web 技术的文章和教程,扩展您在网站开发方面的技能。:这是有关 Ajax 编程模型信息的一站式中心,包括很多文档、教程、论坛、blog、wiki 和新闻。任何 Ajax 的新信息都能在这里找到。,这是有关 Web 2.0 相关信息的一站式中心,包括大量 Web 2.0 技术文章、教程、下载和相关技术资源。您还可以通过
栏目,迅速了解 Web 2.0 的相关概念。
developerWorks: 登录
标有星(*)号的字段是必填字段。
保持登录。
单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件。
在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。
所有提交的信息确保安全。
选择您的昵称
当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。昵称长度在 3 至 31 个字符之间。
您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。
标有星(*)号的字段是必填字段。
(昵称长度在 3 至 31 个字符之间)
单击提交则表示您同意developerWorks 的条款和条件。 .
所有提交的信息确保安全。
文章、教程、演示,帮助您构建、部署和管理云应用。
立即加入来自 IBM 的专业 IT 社交网络。
免费下载、试用软件产品,构建应用并提升技能。
static.content.url=/developerworks/js/artrating/SITE_ID=10Zone=Web developmentArticleID=497588ArticleTitle=JavaScript 中的函数式编程实践publish-date=

我要回帖

更多关于 javascript 数组排序 的文章

 

随机推荐