什么是数组(Array)呢?
- 对象允许存储键值集合,但是在某些情况下使用键值对来访问并不方便;
- 比如说一系列的商品、用户、英雄,包括HTML元素,我们如何将它们存储在一起呢?
- 这个时候我们需要一种有序的集合,里面的元素是按照某一个顺序来排列的;
- 这个有序的集合,我们可以通过索引来获取到它;

这个结构就是数组(Array);
- 数组和对象都是一种保存多个数据的数据结构

我们可以通过[]来创建一个数组:
- 数组是一种特殊的对象类型;

let letters = ['a','b','c','d']

数组的创建方式

	// 1.创建数组的方式
    var names = ['why', 'kobe', 'james', 'curry']
    var product1 = { name: '苹果', price: 10 }
    var products = [
      { name: '鼠标', price: 299 },
      { name: '键盘', price: 1299 },
      { name: '西瓜', price: 59 },
      product1
    ]

    // 2.创建方式二:类 Array
    var arr1 = new Array()
    var arr2 = new Array('abc', 'cba', 'nba')
    console.log(arr1, arr2)

    // 传入一个数字,它默认会当成要创建一个对应长度的数组
    var arr3 = new Array(5)
    console.log(arr3)

数组元素从 0 开始编号(索引index)
- 一些编程语言允许我们使用负数索引,例如 fruits[-1]
- JavaScript 并不支持这种语法

	// 通过索引访问元素
    console.log(names[0]) // 第一个元素
    console.log(names[names.length - 1]) // 最后一个元素

数组的基本操作

访问数组中的元素
- 通过中括号[]访问
- arr.at(i)
- 如果 i>=0,则与 arr[i] 完全相同
- 对于 i 为负数的情况,它则是从数组的尾部向前数

修改数组中的元素

删除和添加虽然也可以直接通过索引来操作,但是一般很少这样

	var names = ['why', 'kobe', 'james', 'curry']

    // 1.访问数组的方式
    console.log(names[0])
    console.log(names.at(0))
    console.log(names[-1])
    console.log(names.at(-1))
    
    // 2.修改数组中的元素
    names[0] = 'why'
    console.log(names)

数组的添加、删除方法(一)

在数组的尾部添加或删除元素
- push 在末端添加元素
- pop 从末端弹出一个元素

数组的首端添加或者删除元素
- shift 取出队列首端的一个元素,整个数组元素向前移动
- unshift 在首端添加元素,其他数组整体向后移动

push/pop 方法运行效率比较快,而 shift/unshift 比较慢

    var names = ['why', 'kobe', 'james', 'curry']

    // 在数组的尾部添加或者删除元素
    // push 方法
    names.push('why', 'what')
    console.log(names)

    // pop方法
    var item1 = names.pop()
    console.log(names)

    // 在数组头部添加和删除元素
    names.unshift('jack', 'kobe')
    console.log(names)

    // shift 方法
    names.shift()
    console.log(names);
  </script>

数组的添加、删除方法(二)

如果我们希望在中间某个位置添加或者删除元素应该如何操作呢?

arr.splice 方法可以说是处理数组的利器,它可以做所有事情:添加,删除和替换元素。

arr.splice 的语法结构如下:
- 从 start 位置开始,处理数组中的元素;
- deleteCount:要删除元素的个数,如果为0或者负数表示不删除;
- item1, item2, ...:在添加元素时,需要添加的元素;

注意:这个方法会修改原数组

length 属性

length属性用于获取数组的长度:
- 当我们修改数组的时候,length 属性会自动更新。

length 属性的另一个有意思的点是它是可写的。
- 如果我们手动增加一个大于默认 length 的数值,那么会增加数组的长度。
- 但是如果我们减少它,数组就会被截断

所以,清空数组最简单的方法就是:arr.length = 0

数组的遍历

普通for循环遍历:

for…in 遍历,获取到索引值:

for…of 遍历,获取到每一个元素:

	var names = ['abc', 'cba', 'nba', 'mba']

    // 获取数组的长度 length
    console.log(names.length)

    // length 属性时可写的
    names.length = 10
    console.log(names)

    // 设置 length 小于原来的元素个数
    names.length = 0
    console.log(names)

    // for 循环
    for (var i = 0; i < names.length; i++) {
      console.log(names[i])
    }

    // for...in
    for (var index in names) {
      console.log(index, names[index])
    }

    // for...of 需要获取索引使用前两个,不需要使用 for...of
    for (var item of names) {
      console.log(item)
    }

数组方法 – slice、cancat、 join

arr.slice 方法:用于对数组进行截取(类似于字符串的 slice 方法)。

- 包含bigin元素,但是不包含end元素

arr.concat方法:创建一个新数组,其中包含来自于其他数组和其他项的值。

arr.join方法: 将一个数组的所有元素连接成一个字符串并返回这个字符串。

数组方法 – 查找元素

arr.indexOf方法: 查找某个元素的索引

- 从fromIndex 开始查找,如果找到返回对应的索引,没有找到返回-1;
- 也有对应的从最后位置开始查找的 lastIndexOf 方法

arr.includes方法:判断数组是否包含某个元素

- 从索引 from 开始搜索 item,如果找到则返回 true(如果没找到,则返回 false)。

findfindIndex 直接查找元素或者元素的索引(ES6之后新增的语法)

    // 1.数组中存放的是原始类型
    var names = ['abc', 'cba', 'nba', 'mba']

    // 1.1 indexOf 可以找到返回对应的索引,没有找到返回 -1
    console.log(names.indexOf('nba'))

    // 2.数组中存放的是对象类型
    var student = [
      { id: 100, name: 'jack', age: 18 },
      { id: 101, name: 'rick', age: 13 },
      { id: 102, name: 'kobe', age: 14 },
      { id: 103, name: 'jerry', age: 16 },
    ]
    
    // 查找的是 id 为 101 的学生 for 循环
    var id = 101
    var stu = null
    for (var i = 0; i < student.length; i++) {
      if (student[i].id === id) {
        stu = student[i]
        break
      }
    }

    // 判断有没有找到对应的学生
    if (stu) {
      console.log('找到了对应的101学生')
    } else {
      console.log('没有找到对应的101学生')
    }

    // 2.2 find方法查找: 高阶函数
    var stu1 = student.find(function (item) {
      if (item.id === 101) return true
    })
    console.log(stu);
	var names = ['abc', 'cba', 'nba']

    // forEach 函数 可以帮助遍历数组
    // myForEach 版本一
    /*
      function myForEach (fn) {
        for (var i = 0; i < names.length; i++) {
          fn(names[i], i, names)
        }
      }

      myForEach(function (item, index, names) {
        console.log('----', item, index, names)
      })
    */
    
    // myForEach 版本二
    /*
        function myForEach (fn, arr) {
          for (var i = 0; i < arr.length; i++) {
            fn(arr[i], i, arr)
          }
        }

        myForEach(function (item, index, names) {
          console.log('----', item, index, names)
        }, names)
        
        myForEach(function (item, index, names) {
          console.log('----', item, index, names)
        }, [1, 2, 3, 56, 7])
     */

    // myForEach 版本三
    /*
      names.myForEach = function myForEach (fn) {
        for (var i = 0; i < this.length; i++) {
          fn(this[i], i, this)
        }
      }

      names.myForEach(function (item, index, names) {
        console.log('----', item, index, names)
      })
    */

    // myForEach版本四
    Array.prototype.myForEach = function (fn) {
      for (var i = 0; i < this.length; i++) {
        fn(this[i], i, this)
      }
    }

    names.myForEach(function (item, index, names) {
      console.log('----', item, index, names)
    })

    var student = [
      { id: 100, name: 'jack', age: 18 },
      { id: 101, name: 'rick', age: 13 },
      { id: 102, name: 'kobe', age: 14 },
      { id: 103, name: 'jerry', age: 16 },
    ]
    
    student.myForEach(function (item, index, student) {
      console.log('++++', item, index, student)
    })
    // 1.原始类型的查找
    /*
    var names = ['abc', 'cba', 'nba', 'mba']
    
    var findName = names.find(function (item, index, arr) {
      console.log(item, index, arr)
      return item === 'nba'
    })
    
    console.log(findName)
    */
  
    // 2.数组中对象类型的查找
    var student = [
      { id: 100, name: 'jack', age: 18 },
      { id: 101, name: 'rick', age: 13 },
      { id: 102, name: 'kobe', age: 14 },
      { id: 103, name: 'jerry', age: 16 },
    ]
    
    /*
      var findStu = student.find(function (item) {
        return item.id === 101
      })
      console.log(findStu)
     */

    // 3.自己实现 myFind

    Array.prototype.myFind = function (fn) {
      // var item = undefined
      for (var i = 0; i < this.length; i++) {
        var isFlag = fn(this[i], i, this)
        if (isFlag) {
          /* item = this[i]
          break */
          return this[i]
        }
      }
      // return item
    }

    var okStu = student.myFind(function (item) {
      if (item.id === 101) {
        return true
      }
    })
    console.log(okStu);

数组的排序 – sort/reverse

sort方法也是一个高阶函数,用于对数组进行排序,并且生成一个排序后的新数组:


- 如果 compareFunction(a, b) 小于 0 ,那么 a 会被排列到 b 前面;
- 如果 compareFunction(a, b) 等于 0 , a 和 b 的相对位置不变;
- 如果 compareFunction(a, b) 大于 0 , b 会被排列到 a 前面;
- 也就是说,谁小谁排在前面;

reverse() 方法将数组中元素的位置颠倒,并返回该数组。

    var nums = [20, 4, 10, 16, 100, 88]

    // sort:排序
    nums.sort(function (item1, item2) {
      // item1 和 item2 比较
      // item1 - item2 返回的是 整数
      // return item1 - item2 // 升序
      return item2 - item1 // 降序
    })

    console.log(nums)
    console.log(nums.reverse())

    // 复杂类型排序
    var student = [
      { id: 100, name: 'jack', age: 18 },
      { id: 101, name: 'rick', age: 13 },
      { id: 102, name: 'kobe', age: 22 },
      { id: 103, name: 'jerry', age: 16 },
    ]

    student.sort(function (item1, item2) {
      return item1.age - item2.age
    })

    console.log(student)

数组的其他高阶方法

arr.forEach
- 遍历数组,并且让数组中每一个元素都执行一次对应的方法;

arr.map
- map() 方法创建一个新数组;
- 这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成;

arr.filter
- filter() 方法创建一个新数组;
- 新数组中只包含每个元素调用函数返回为true的元素;

arr.reduce
- 用于计算数组中所有元素的总和;
- 对数组中的每个元素按序执行一个由您提供的 reducer 函数;
- 每一次运行 reducer 会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值;

	// 1.forEach函数
    var names = ["abc", "cba", "nba", "mba"]

    // 三种方式, 新增一种方式
    names.forEach(function(item) {
      console.log(item, this)
    }, { name: "why" })

    // 2.filter函数: 过滤
    var nums = [11, 20, 55, 100, 88, 32]
    // 2.1. for循环实现
    var newNums = []
    for (var item of nums) {
      if (item % 2 === 0) {
        newNums.push(item)
      }
    }

    // 2.2. filter实现
    var newNums = nums.filter(function(item) {
      return item % 2 === 0
    })

    console.log(newNums)

    // 3.map函数: 映射
    var nums = [11, 20, 55, 100, 88, 32]
    var newNums = nums.map(function(item) {
      return item * item
    })

    console.log(newNums)

    // 4.reduce
    var nums = [11, 20, 55, 100, 88, 32]
    var result = 0
    for (var item of nums) {
      result += item
    }

    console.log(result)

    // 第一次执行: preValue->0 item->11

    // 第二次执行: preValue->11 item->20

    // 第三次执行: preValue->31 item->55

    // 第四次执行: preValue->86 item->100

    // 第五次执行: preValue->186 item->88

    // 第六次执行: preValue->274 item->32

    // 最后一次执行的时候 preValue + item, 它会作为reduce的返回值

    // initialValue: 初始化值, 第一次执行的时候, 对应的preValue

    // 如果initialValue没有传呢?
    var result = nums.reduce(function(preValue, item) {
      console.log(`preValue:${preValue} item:${item}`)
      return preValue + item
    }, 0)
    
    console.log(result)
    
    // reduce 练习

    var products = [
      { name: "鼠标", price: 88, count: 3 },
      { name: "键盘", price: 200, count: 2 },
      { name: "耳机", price: 9.9, count: 10 },
    ]

    var totalPrice = products.reduce(function(preValue, item) {
      return preValue + item.price * item.count
    }, 0)

    console.log(totalPrice)

    // 综合练习:

    var nums = [11, 20, 55, 100, 88, 32]
    
    // 过滤所有的偶数, 映射所有偶数的平方, 并且计算他们的和
    var total = nums.filter(function(item) {
      return item % 2 === 0
    }).map(function(item) {
      return item * item
    }).reduce(function(preValue, item) {
      return preValue + item
    }, 0)
    
    console.log(total)

    var total = nums.filter(item => item % 2 === 0)
                    .map(item => item * item)
                    .reduce((preValue, item) => preValue + item, 0)
                    
    console.log(total)

Q.E.D.