JavaScript学习笔记(三) 数组

严格来说,在 JavaScript 中并不存在数组这个数据类型,但是 JavaScript 却提供了一种具有数组特性的对象

并且通过一定的封装,以及提供一系列的语法糖,让这个对象用起来像真正的数组一样方便

1、创建数组

(1)数组字面量

数组字面量由零个或多个用逗号分隔的表达式组成,每个表达式的值可以是任意类型,所有表达式用方括号括起来

> // 创建一个空数组
> var empty = []
> // 创建一个带有内容的数组
> var fruit = ['apple', 'banana', 'cherry']

检查一下刚刚创建的数组,发现它是一个 object 类型

> typeof fruit
// 'object'

在创建数组的时候,数组中的每个元素都会得到一个默认的属性名,第一个为 '0',第二个为 '1',以此类推

最终,属性名和元素值以键值对的形式存储下来,类似于下面直接用对象直接量创建的一个对象

> var fruit = {'0': 'apple', '1': 'banana', '2': 'cherry'}

当然,两者(使用数组直接量创建数组 和 使用对象直接量创建对象)还是有很多不同之处的

  • 数组继承自 Array.prototype,对象继承自 Object.prototype,两者具有的方法有所不同
  • 数组拥有一个神奇的 length 属性,而对象没有

(2)new Array()

可以使用 new 运算符加上 Array 构造函数创建一个数组

> // 创建一个空数组
> var empty = new Array()

2、元素的增删改查

(0)数组的索引

对于常规的数组而言,数组的索引是从零开始的连续增加的正整数,但是在 JavaScript 中情况就不一样了

因为数组也是对象的一种,所以理论上我们可以使用任意合法的字符串索引数组,就像索引对象属性一样

那么怎么区分数组索引和对象属性呢?

JavaScript 规定,只有在 [0, 2**32) 之间的整数才能作为数组索引,否则只能将其当作对象属性

(1)元素的读取(查)

使用方括号操作符( [] )可以访问数组中的元素

方括号的左边是数组的引用,方括号的里面是一个返回非负整数的任意表达式,作为数组的索引

> var fruit = ['apple', 'banana', 'cherry']
> fruit[0]
// 'apple'

不要忘了哦,数组是对象的一种特殊形式,所以使用方括号访问数组元素就像使用方括号访问对象属性一样

JavaScript 会把数字索引值转化为字符串索引值,然后将其作为属性名使用

(2)元素的设置(改)

设置元素的值同样可以使用方括号运算符

> var fruit = ['apple', 'banana', 'cherry']
> fruit[0] = 'almond'
> fruit[0]
// 'almond'

(3)元素的删除(删)

  • delete 运算符:在原数组中将指定索引的元素值设置为 undefined
> var drink = ['milk', 'tea', 'coffee']
> delete drink[1]
> drink[1]
// undefined
> drink[2]
// coffee
  • pop 方法:在原数组中删除最后一个元素,并返回删除的元素
> var drink = ['milk', 'tea', 'coffee', 'beer']
> var drink_popped = drink.pop()
> drink
// ['milk', 'tea', 'coffee']
> drink_popped
// 'beer'
  • shift 方法:在原数组中删除最前一个元素,并返回删除的元素
> var drink = ['milk', 'tea', 'coffee', 'beer']
> var drink_shifted = drink.shift()
> drink
// ['tea', 'coffee', 'beer']
> drink_shifted
// 'milk'
  • splice 方法:在原数组中删除指定元素,第一个参数指定起始索引,第二个参数指定长度,返回删除的元素
> var drink = ['milk', 'tea', 'coffee', 'coke', 'sprite', 'beer']
> var drink_spliced = drink.splice(3, 2)
> drink
// ['milk', 'tea', 'coffee', 'beer']
> drink_spliced
// ['coke', 'sprite']

(4)元素的增加(增)

  • 添加元素最简单的方式就是给新索引赋值
> var food = ['rice', 'meat', 'vegetable']
> food[3] = 'noodle'
> food
// ['rice', 'meat', 'vegetable', 'noodle']
  • push 方法:在原数组的最后添加一个元素,返回新数组中元素的个数
> var food = ['rice', 'meat', 'vegetable']
> var food_length = food.push('bread')
> food
// ['rice', 'meat', 'vegetable', 'bread']
> food_length
// 4
  • unshift 方法:在原数组的最前添加一个元素,返回新数组中元素的个数
> var food = ['rice', 'meat', 'vegetable']
> var food_length = food.unshift('bread')
> food
// ['bread', 'rice', 'meat', 'vegetable']
> food_length
// 4
  • splice 方法:除了删除数组中的元素,splice 方法还可以通过第三个可选的参数添加元素
> var food = ['rice', 'meat', 'vegetable']
> var food_spliced = food.splice(2, 0, 'fish')
> food
// ['rice', 'meat', 'fish', 'vegetable']
> food_spliced
// []

3、元素的遍历

由于数组也是对象的一种,所以我们可以使用遍历对象的方式来遍历数组,常用的有 for-in 循环和 for-of 循环

> var fruit = ['apple', 'banana', 'cherry']
> fruit[5] = 'filbert'
> // for-in 循环
> for (let index in fruit) {
    console.log(fruit[index])
}
// apple
// banana
// cherry
// filbert
> // for-of 循环
> for (let item of fruit) {
    console.log(item)
}
// apple
// banana
// cherry
// undefined
// undefined
// filbert

4、数组的 length 属性

JavaScript 数组 length 属性的值不一定等于数组中元素的个数,它实际上是等于数组中最大的合法索引加 1

在这里有一条规则,那就是 在数组中肯定找不到一个元素的索引值大于或等于它的长度

为了维持这个规则,length 属性有许多有趣的行为

  • 如果为一个数组元素赋值,并且它的索引 i 大于或等于现有的数组长度,那么 length 属性的值将设置为 i + 1
> var alphabet = ['a', 'b', 'c']
> alphabet.length
// 3
> alphabet[4] = 'e'
> alphabet.length
// 5
  • 如果为一个数组元素赋值,并且它的索引不是一个合法的数组索引,那么 length 属性的值将不会发生改变
> var alphabet = ['a', 'b', 'c']
> alphabet.length
// 3
> alphabet[-1] = 'z'
> alphabet.length
// 3
  • 如果设置 length 属性为一个小于当前长度的非负整数 n 时,数组中那些索引值大于或等于 n 的元素将被删除
> var alphabet = ['a', 'b', 'c']
> alphabet.length
// 3
> alphabet.length = 1
> alphabet
// ['a']

5、数组的方法

JavaScript 提供了一套作用于数组的方法,这些方法是储存在 Array.prototype 的函数

(1)常规的数组方法

  • join:将数组中的所有元素都转化为字符串并拼接在一起,返回拼接后的字符串

    可以通过一个可选的参数(字符串类型)指定分隔符,默认使用逗号

> var numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
> var numbers_join = numbers.join()
> numbers_join
// '0,1,2,3,4,5,6,7,8,9'
  • reverse:将数组中的元素倒序排列,返回排序后的数组
> var numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
> var numbers_reverse = numbers.reverse()
> numbers_reverse
// [ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ]
  • sort:将数组中的元素按照一定的规则排序,返回排序后的数组

    可以通过一个可选的参数(函数类型)指定元素之间的比较规则,默认为字典序

> var numbers = [111, 3, 22]
> ns1 = numbers.sort() // 转化为字符串,按照字典序排列
> ns2 = numbers.sort(function(a, b) { return a < b }) // 按照数值从大到小排列
> ns3 = numbers.sort(function(a, b) { return a > b }) // 按照数值从小到大排列
> ns1
// [ 111, 22, 3 ]
> ns2
// [ 111, 22, 3 ]
> ns3
// [ 3, 22, 111 ]
  • concat:拼接两个数组的元素,返回拼接之后的数组
> var a = [1, 2, 3]
> var b = [4, 5, [7], [8, 9]]
> var c = a.concat(b)
> var d = b.concat(a)
> c
// [1, 2, 3, 4, 5, [7], [8, 9]]
> d
// [4, 5, [7], [8, 9], 1, 2, 3]
  • slice:返回指定范围的数组片段

    两个参数分别指定起止位置,省略第二个参数默认为数组最后,同时省略第一个参数默认为数组最前

> var a = [1, 2, 3, 4, 5]
> var b = a.slice(1, 4)
> var c = a.slice(2) // 省略第二个参数
> var d = a.slice() // 同时省略第二个和第一个参数
> b
// [ 2, 3, 4 ]
> c
// [ 3, 4, 5 ]
> d
// [ 1, 2, 3, 4, 5 ]
  • includes:检查数组中是否包含特定的元素,有的话返回 true,没有的话返回 false
> var numbers = [1, 2, 3, 4, 5]
> numbers.includes(0)
// false
> numbers.includes(1)
// true
  • indexOf:检查数组中是否包含特定的元素,有的话返回对应的索引,没有的话返回 -1
> var numbers = [1, 2, 3, 4, 5]
> numbers.indexOf(0)
// -1
> numbers.indexOf(1)
// 0

(2)ECMAScript5 中的数组方法

ECMAScript5 中新定义的数组方法具有一些类似的特性,这里先做介绍

这些方法大都接收一个函数作为参数,并且对数组中的每个元素都调用一次该函数

该函数接收三个参数,分别是数组元素、元素索引和数组本身

(1)forEach

> var numbers = [1, 3, 5, 7, 9]
> // 计算数组中所有元素的总和
> var sum = 0
> numbers.forEach(function(value){
    sum += value
})
> sum
// 25
> // 将数组中的每个元素加 1
> numbers.forEach(function(value, index, array){
    array[index] = value + 1
})
> numbers
// [2, 4, 6, 8, 10]

(2)map

> var numbers = [1, 2, 3, 4, 5]
> // 将数组中的每个元素乘 2,并返回一个新的数组
> var numbers_doubled = numbers.map(function(number){
    return (number * 2)
})
> numbers_doubled
// [2, 4, 6, 8, 10]

(3)filter

> var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
> // 留下偶数元素
> var even_numbers = numbers.filter(function(number){
    return (number % 2 === 0)
})
> even_numbers
// [2, 4, 6, 8, 10]

(4)every / some

> function isPrime(val) {
    if (val < 2) {
        return false
    } else if (val === 2) {
        return true
    } else { // val > 2
        if (val % 2 === 0) {
            return false
        } else { // val % 2 !== 0
            for (let i = 3; i * i <= val; i += 2) {
                if (val % i === 0) {
                    return false
                }
            }
            return true
        }
    }
}
> // 判断数组中的每个元素是否都是素数
> [2, 3, 5, 7].every(function(value){
    return isPrime(value)
})
// true
> [2, 3, 5, 7, 9].every(function(value){
    return isPrime(value)
})
// false
> // 判断数组中是否存在一个元素是素数
> [4, 6, 8, 9].some(function(value){
    return isPrime(value)
})
// false
> [2, 4, 6, 8, 9].some(function(value){
    return isPrime(value)
})
// true

这里给大家出一道题目,请写出下面程序的执行结果

['1', '3', '10'].map(parseInt)

输出的结果是 [1, NaN, 2],大家答对了吗

解决这道题目有两个关键的知识点,一个是 map 函数的原理,一个是 parseInt 函数的原理

map 函数的原理,就是将在参数中指定的函数作用于数组的每一个元素,然后把这些结果组成一个数组返回

它接收的第一个参数是数组的当前元素,第二个参数是该元素的索引,第三个参数是数组本身

实际上,这个程序会返回这样的结果 [parseInt('1', 0), parseInt('3', 1), parseInt('10', 2)]

好,下面我们再来看看 parseInt 函数的作用,它的作用其实就是将一个字符串转换成十进制数字

它接收的第一个参数是被解析的字符串,第二个参数表示要使用的基数,这个值可以是 0 或者在 2 ~ 36 之间

parseInt('1', 0):基数为 0,默认使用十进制解析字符串,将十进制的数字 1 转换为十进制数字还是 1

parseInt('3', 1):基数既不为 0,也不在范围 2 ~ 36 之间,所以返回 NaN

parseInt('10', 2):基数为 2,因此使用二进制解析字符串,将二进制的数字 10 转换为十进制数字是 2

所以最终的结果就是 [1, NaN, 2]

【 阅读更多 JavaScript 系列文章,请看 JavaScript学习笔记

posted @ 2019-11-10 21:35  半虹  阅读(167)  评论(0编辑  收藏  举报