风趣的JavaScript原生数组函数

复制:.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 }]

此外,.slice的另一个通常用法是从一个参数列表中删除一些元素,这可以将类数组对象转换为真正的数组。

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')

在JavaScript中,可以通过两种方式创建数组,构造函数和数组直接量,
其中后者为首选方法。数组对象继承自Object.prototype,对数组执行typeof操作符返回‘object’而不是‘array’。然而执行[]
instanceof
Array返回true。此外,还有类数组对象是问题更复杂,如字符串对象,arguments对象。arguments对象不是Array的实例,但却有个length属性,并且值能通过索引获取,所以能像数组一样通过循环操作。

栈和队列.pop,.push,.shift和.unshift

每个人都知道向数组添加元素用.push。但你知道一次可以添加多个元素吗?如下[].push(‘a’,
‘b’, ‘c’, ‘d’, ‘z’)。

.pop方法和.push成对使用,它返回数组的末尾元素并将元素从数组移除。如果数组为空,返回void
0(undefined)。使用.push和.pop我们能轻易模拟出LIFO(后进先出或先进后出)栈。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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() // <- 3

相反,我们可以用.unshift 和 .shift模拟FIFO(先进先出)队列。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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() // <- 1

用.shift或.pop能很容易遍历数组元素,并在操作过程中清空数组。

1 2 3 4 5 6 7 8 list = [1,2,3,4,5,6,7,8,9,10]   while (item = list.shift()) {     console.log(item) }   list // <- []

 

  • 循环:.forEach
  • 判断:.some和.every
  • 区分.join和.concat
  • 栈和队列的实现:.pop, .push, .shift,和 .unshift
  • 模型映射:.map
  • 查询:.filter
  • 排序:.sort
  • 计算:.reduce和.reduceRight
  • 复制:.slice
  • 强大的.splice
  • 查找:.indexOf
  • 操作符:in
  • 走近.reverse

循环.forEach

这是JavaScript原生数组方法中最简单的方法。不用怀疑,IE7和IE8不支持此方法。

forEach方法需要一个回调函数,数组内的每个元素都会调用一次此方法,此方法需要三个参数如下:

  • value 当前操作的数组元素
  • index 当前操作元素的数组索引
  • 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循环,抛出异常是不明智的方法。幸运的是,我们有其他的方法中断操作。

万能的.splice

.splice是我最喜欢的原生数组函数之一。它允许你删除元素,插入新元素,或在同一位置同时进行上述操作,而只使用一个函数调用。注意和.concat和.slice不同的是.splice函数修改原数组。

1 2 3 4 5 6 7 8 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]   spliced // <- [8, 8, 8, 8]

你可能已经注意到,它也返回被删除的元素。这在你想遍历数组的某一个区段并且删除时派上用场。

1 2 3 4 5 6 7 8 9 10 11 12 13 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。我经常使用.indexOf的情况是当我有比较时,例如:a
=== ‘a’ || a === ‘b’ || a ===
‘c’,或者只有两个比较,此时,可以使用.indexOf:[‘a’, ‘b’,
‘c’].indexOf(a) !== -1。

注意,如果提供的引用相同,.indexOf也能查找对象。第二个可选参数用于指定开始查找的位置。

var a = { foo: 'bar' }
var b = [a, 2]
console.log(b.indexOf(1))
// <- -1
console.log(b.indexOf({ foo: 'bar' }))
// <- -1
console.log(b.indexOf(a))
// <- 0
console.log(b.indexOf(a, 1))
// <- -1
b.indexOf(2, 1)
// <- 1

如果你想从后向前搜索,可以使用.lastIndexOf。

查询.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
})
// <- [1]

[void 0, null, false, '', 1].filter(function (value) {
    return !value
})
// <- [void 0, null, false, '']

断言.some和.every

如果你曾经用过.NET的枚举,这些方法的名字和.Any(x => x.IsAwesome) 和 .All(x => x.IsAwesome)非常相似。

这些方法和.forEach类似,需要一个包含value,index,和array三个参数的回调函数,并且也有一个可选的第二个上下文参数。MDN对.some的描述如下:

some将会给数组里的每一个元素执行一遍回调函数,直到有一个回调函数返回true为止。如果找到目标元素,some立即返回true,否则some返回false。回调函数只对已经指定值的数组索引执行;它不会对已删除的或未指定值的元素执行。

1 2 3 4 5 6 7 8 9 10 11 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) // <- 12   satisfied // <- true

注意,当回调函数的value < 10
条件满足时,中断函数循环。.every的工作行为类似,但中断的条件是回调函数返回false而不是true。

永利官网ylg客户端 1

永利官网ylg客户端 2

复制.slice

和.concat类似,调用.slice缺省参数时,返回原数组的浅拷贝。slice函数需要两个参数,一个是开始位置和一个结束位置。

Array.prototype.slice能被用来将类数组对象转换为真正的数组。

1 2 Array.prototype.slice.call({ 0: 'a', 1: 'b', length: 2 }) // <- ['a', 'b']

除此之外,另一个常见用途是从参数列表中移除最初的几个元素,并将类数组对象转换为真正的数组。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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>

 

操作符:in

在面试中新手容易犯的错误是混淆.indexOf和in操作符:

var a = [1, 2, 5]
1 in a
// <- true, but because of the 2!
5 in a
// <- false

问题是in操作符是检索对象的键而非值。当然,这在性能上比.indexOf快得多。

var a = [3, 7, 6]
1 in a === !!a[1]
// <- 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()
// <- 3

相反,我们可以用.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()
// <- 1

用.shift或.pop能很容易遍历数组元素,并做一些操作。

list = [1,2,3,4,5,6,7,8,9,10]

while (item = list.shift()) {
    console.log(item)
}

list
// <- []

查询.filter

filter对每个数组元素执行一次回调函数,并返回一个由回调函数返回true的元素
组成的新数组。回调函数只会对已经指定值的数组项调用。

用法例子:.filter(fn(value, index, array),
thisArgument)。把它想象成.Where(x => x.IsAwesome) LINQ
expression(如果你熟悉C#),或者SQL语句里面的WHERE。考虑到.filter仅返回callback函数返回真值的值,下面是一些有趣的例子。没有传递给回调函数测试的元素被简单的跳过,不会包含进返回的新数组里。

1 2 3 4 5 6 7 8 9 [void 0, null, false, '', 1].filter(function (value) {     return value }) // <- [1]   [void 0, null, false, '', 1].filter(function (value) {     return !value }) // <- [void 0, null, false, '']

 

在JavaScript中,创建数组可以使用Array构造函数,或者使用数组直接量[],后者是首选方法。Array对象继承自Object.prototype,对数组执行typeof操作符返回object而不是array。然而,[]
instanceof
Array也返回true。也就是说,类数组对象的实现更复杂,例如strings对象、arguments对象,arguments对象不是Array的实例,但有length属性,并能通过索引取值,所以能像数组一样进行循环操作。
在本文中,我将复习一些数组原型的方法,并探索这些方法的用法。

译者注

本文为翻译文章,原文为“Fun with JavaScript Native Array
Functions”。

永利官网ylg客户端 3 支持我继续翻译吧。

永利官网ylg客户端 4更多文章请访问的我的博客。

永利官网ylg客户端 5 关注我的微博吧。

循环.forEach

这是JavaScript原生数组方法中最简单的方法。不用怀疑,IE7和IE8不支持此方法。

forEach方法需要一个回调函数,数组内的每个元素都会调用一次此方法,调用时会传入的三个参数如下:

  • value 当前操作的数组元素
  • index 当前操作元素的数组索引
  • array 当前数组的引用

此外,可以传递可选的第二个参数,作为每个调用函数的上下文(this)。

1 2 3 4 5 6 ['_', '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[ylg娱乐官网,2] + ” + out[n]。

我们不能用break中断forEach循环,抛出异常是不明智的方法。幸运的是,我们有其他的方法中断操作。

 

排序:.sort(compareFunction)

如果没有提供compareFunction,元素会被转换成字符串并按照字典排序。例如,”80″排在”9″之前,而不是在其后。

永利官网ylg客户端,跟大多数排序函数类似,Array.prototype.sort(fn(a,b))需要一个包含两个测试参数的回调函数,其返回值如下:

  • a在b之前则返回值小于0
  • a和b相等则返回值是0
  • a在b之后则返回值小于0

    [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]

断言.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)
// <- 12

satisfied
// <- true

注意,当回调函数的value < 10
条件满足时,中断函数循环。.every的工作行为类似,但回调函数要返回false而不是true。

查找.indexOf

通过.indexOf,我们可以查找数组元素的位置。如果没有匹配元素则返回-1。我发现我用的很多的一个模式是连续比较,例如a
=== ‘a’ || a === ‘b’ || a ===
‘c’,或者即使只有两个结果的比较。在这种情况下,你也可以使用.indexOf,像这样:[‘a’,
‘b’, ‘c’].indexOf(a) !== -1。

注意,对于对象来说,只有指向同一个对象的引用才能被识别出。第二个参数是开始查询的起始位置。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var a = { foo: 'bar' } var b = [a, 2]   console.log(b.indexOf(1)) // <- -1   console.log(b.indexOf({ foo: 'bar' })) // <- -1   console.log(b.indexOf(a)) // <- 0   console.log(b.indexOf(a, 1)) // <- -1   b.indexOf(2, 1) // <- 1

如果你想从后向前搜索,.lastIndexOf能派上用场。

发表评论

电子邮件地址不会被公开。 必填项已用*标注