js数组方法总结

文章目录

js数组方法总结

JavaScript 中 Array 对象用于在单一变量中存储多个元素。

数组方法概括

方法名功能原数组是否改变
Array()构造函数用于创建 Array 对象。-
Array[Symbol.species]一个静态访问器属性,用于返回构造函数,构造函数用于构造数组方法返回值。-
Array.prototype[Symbol.unscopables]用于指定对象值,其对象自身和继承的从关联对象的 with 环境绑定中排除的属性名称。-
Array.lengthlength 是 Array 的实例属性,表示该数组中元素的个数。Y
Array.of()创建一个包含所有传入参数的数组-
Array.prototypeSymbol.iterator每一个对象定义了默认的迭代器,该迭代器可以被 for…of 循环使用。-
Array.prototype.at()接收一个整数值并返回该索引对应的元素-
Array.prototype.concat()合并两个或多个数组,返回一个新数组。N
Array.prototype.constructor数组的构建函数的属性,constructor()是数组的引用。-
Array.prototype.copyWithin()浅复制数组的一部分到同一数组中的另一个位置,并返回它,原数组改变但长度不变Y
Array.prototype.entries()返回一个新的数组迭代器 (en-US)对象-
Array.prototype.every()判断数组中每一项都是否满足条件-
Array.prototype.fill()用一个固定值填充一个数组中从起始索引(默认为 0)到终止索引(默认为 array.length)内的全部元素Y
Array.prototype.filter()一次过滤,用于把数组的某些元素过滤掉,然后返回剩下的元素。N
Array.prototype.find()返回数组中满足条件的第一个元素的值。否则返回 undefined。-
Array.prototype.findIndex()返回数组中满足条件的第一个元素的索引。否则返回 -1。-
Array.prototype.findLast()相反的顺序迭代数组,返回数组中满足条件的第一个元素的值。否则返回 undefined。-
Array.prototype.findLastIndex()相反的顺序迭代数组,返回数组中满足条件的第一个元素的索引。否则返回 -1。-
Array.prototype.flat()扁平化数组,按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。N
Array.prototype.flatMap()扁平化数组,对数组中的每个元素应用给定的回调函数,然后将结果展开一级,返回一个新数组。N
Array.prototype.forEach()用于调用数组的每一个元素,并将元素传递给回调函数。-
Array.from()从可迭代或类数组对象创建一个新的浅拷贝的数组实例。-
Array.prototype.includes()判断一个数组是否包含一个指定的值,包含则返回 true,否则返回 false。-
Array.prototype.indexOf()返回数组中第一次出现给定元素的下标,如果不存在则返回 -1。-
Array.isArray()判断传递的值是否是一个 Array,如果是则返回true,否则false。-
Array.prototype.join()将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串,用逗号或指定的分隔符字符串分隔。-
Array.prototype.keys()返回一个包含数组中每个索引的键的新数组迭代器 (en-US)对象。-
Array.prototype.lastIndexOf()返回数组中给定元素最后一次出现的索引,如果不存在则返回 -1。-
Array.prototype.map()返回一个由原数组中的每个元素调用一个指定方法后的返回值组成的新数组。N
Array.prototype.pop()从数组中删除最后一个元素,并返回该元素的值。Y
Array.prototype.push()将指定的元素添加到数组的末尾,并返回新的数组长度。Y
Array.prototype.reduce()接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。N
Array.prototype.reduceRight()接收一个函数作为累加器,数组中的每个值(从右到左)开始缩减,最终计算为一个值。N
Array.prototype.reverse()反转数组中元素的位置,改变了数组,并返回该数组的引用。Y
Array.prototype.shift()从数组中删除第一个元素,并返回该元素的值。Y
Array.prototype.slice()返回一个从原数组索引 start 到 end 元素的浅拷贝(包括 start,不包括 end)得到的新的数组。N
Array.prototype.some()判断数组中是否存在满足条件的项。-
Array.prototype.sort()对数组的元素进行排序,并返回对相同数组的引用。Y
splice通过移除或者替换已存在的元素和/或添加新元素就地改变一个数组的内容。Y
Array.prototype.toLocaleString()将每个数组元素转化为字符串,并且使用本地化分隔符将这些字符串连接起来生成最终的字符串。N
Array.prototype.toReversed()返回一个元素顺序相反的新数组。N
Array.prototype.toSorted()返回一个新数组,其元素按指定的定义排序顺序的函数排列。N
Array.prototype.toSpliced()返回一个新数组,并在给定的索引处删除和/或替换了一些元素。N
Array.prototype.toString()返回包含所有数组值的字符串,以逗号分隔。N
Array.prototype.unshift()将指定元素添加到数组的开头,并返回数组的新长度。Y
Array.prototype.values()返回一个包含数组所有值的迭代器对象。-
Array.prototype.with()返回一个指定索引处的值被新值替换的新数组。N
hasOwnProperty()用来检测一个属性是否是对象的自有属性。-
isPrototypeOf用于测试一个对象是否存在于另一个对象的原型链上。-
propertyIsEnumerable()检测一个属性是否可枚举-
valueOf()返回指定对象的原始值,若对象没有原始值,则将返回对象本身。-

数组方法详解

Array()

Array() 构造函数用于创建 Array 对象。

调用 Array() 时可以使用或不使用 new。两者都会创建一个新的 Array 实例。

如果向Array()构造函数传入多个参数,则会创建一个包含给定元素的新 Array。

如果只有一个参数且其值不在 0 到 2³² - 1(包括)之间,则会触发异常。

如果只有一个参数且其在 0 到 2³² - 1(包括)之间,数组的长度为传入的参数,该数组不包含任何实际的元素。

也可以通过使用数组字面量创建数组。

// Array()
let arr = new Array()
console.log(arr) //[]
let arr1 = new Array(2)
console.log(arr1) //[empty × 2]
let arr2 = new Array('1', 2, 4)
console.log(arr2) // ['1', 2, 4]
let arr3 = new Array(-1)
console.log(arr3) //数组方法总结.js:8 Uncaught RangeError: Invalid array length

let list = Array()
console.log(list) //[]
let list1 = Array(2)
console.log(list1) //[empty × 2]
let list2 = Array('1', 2, 4)
console.log(list2) // ['1', 2, 4]
let list3 = Array(-1)
console.log(list3) //数组方法总结.js:8 Uncaught RangeError: Invalid array length

// 数组字面量
let array = []
console.log(array) //[]
let array1 = ['1', 2, 4]
console.log(array1) // ['1', 2, 4]

Array[Symbol.species]

[Symbol.species]是一个静态访问器属性,用于返回构造函数,构造函数用于构造数组方法返回值。

Symbol.species 是个函数值属性,其被构造函数用以创建派生对象,

Symbol.species访问器属性允许子类覆盖对象的默认构造函数。

let species = Array[Symbol.species]
console.log(species) //ƒ Array() { [native code] }
let species1 = [][Symbol.species]
console.log(species1) //undefined
let species2 = new Array(12, 1)[Symbol.species]
console.log(species2) //undefined

// 在扩展数组类 MyArray 上返回 Array 对象。
class MyArray extends Array {
  // 重写 MyArray 的 species 属性到父类 Array 的构造函数
  static get [Symbol.species]() {
    return Array
  }
}

// 当使用例如 map() 这样的方法返回默认的构造函数时,
// 返回父级的 Array 对象,以取代 MyArray 对象。
var a = new MyArray(1, 2, 3)
var mapped = a.map(x => x * x)

console.log(mapped instanceof MyArray) // false
console.log(mapped instanceof Array) // true

let value = MyArray[Symbol.species]
console.log(value) //ƒ Array() { [native code] }
let value1 = new MyArray(1, 2, 3)[Symbol.species]
console.log(value1) //undefined

Array.prototype[Symbol.unscopables]

Symbol.unscopables 指用于指定对象值,其对象自身和继承的从关联对象的 with 环境绑定中排除的属性名称。

可以在任何对象上定义 @@unscopables symbol (Symbol.unscopables),用于排除属性名称并与 with 环境绑定在一起作为词法变量公开。 请注意,如果使用 Strict mode,语句将不可用,并且可能也不需要 symbol。

在 unscopables 对象上设置属性为 true,将使其 unscopable并且因此该属性也将不会在词法环境变量中出现。 如果设置属性为 false ,则将使其可 scopable 并且该属性会出现在词法环境变量中。

也可以自定义设置对象 unscopables 。

var keys = []

with (Array.prototype) {
  keys.push('something')
  console.log(keys) //['something']
  let flag = keys.includes(1)
  console.log(flag) //false
}

with (keys) {
  push('something2')
  console.log(keys) //['something', 'something2']
  let flag = includes(1) //Uncaught ReferenceError: includes is not defined
  console.log(flag)
}

let unscopables = Array.prototype[Symbol.unscopables]
console.log(unscopables) //{ at: true, copyWithin: true, entries: true, fill: true, find: true, findIndex: true, findLast: true, findLastIndex: true, flat: true, flatMap: true, includes: true, keys: true, toReversed: true, toSorted: true, toSpliced: true, values: true }
let unscopables1 = keys[Symbol.unscopables]
console.log(unscopables1) //{ at: true, copyWithin: true, entries: true, fill: true, find: true, findIndex: true, findLast: true, findLastIndex: true, flat: true, flatMap: true, includes: true, keys: true, toReversed: true, toSorted: true, toSpliced: true, values: true }

// 设置对象 unscopables
Array.prototype[Symbol.unscopables].includes = false
keys[Symbol.unscopables].flatMap = false
let unscopables2 = Array.prototype[Symbol.unscopables]
console.log(unscopables2) //{ at: true, copyWithin: true, entries: true, fill: true, find: true, findIndex: true, findLast: true, findLastIndex: true, flat: true, flatMap: false, includes: false, keys: true, toReversed: true, toSorted: true, toSpliced: true, values: true }
let unscopables3 = keys[Symbol.unscopables]
console.log(unscopables3) //{ at: true, copyWithin: true, entries: true, fill: true, find: true, findIndex: true, findLast: true, findLastIndex: true, flat: true, flatMap: false, includes: false, keys: true, toReversed: true, toSorted: true, toSpliced: true, values: true }

with (keys) {
  push('something3')
  console.log(keys) //['something', 'something2', 'something3']
  let flag = includes(1)
  console.log(flag) //false
}

// 自定义设置对象 unscopables
var obj = {
  foo: 1,
  bar: 2
}

obj[Symbol.unscopables] = {
  foo: false,
  bar: true
}

with (obj) {
  console.log(foo) // 1
  console.log(bar) // ReferenceError: bar is not defined
}

Array.length

length 是 Array 的实例属性,表示该数组中元素的个数。该值是一个无符号 32 位整数,并且其数值总是大于数组最大索引。

// Array.length
const listA = [1, 2, 3]
const listB = new Array(6)
console.log(listA.length) // 3
console.log(listB.length) // 6

将 length 设置为无效值(例如负数或非整数或者大于等于2 ** 32)会引发 RangeError 异常

// 将 length 设置为无效值(例如负数或非整数或者大于等于2 ** 32)会引发 RangeError 异常
listB.length = 2 ** 32 // RangeError: Invalid array length
listB.length = -1 // RangeError: Invalid array length
listB.length = '1' // RangeError: Invalid array length
// 负数是不允许的
const listC = new Array(-100) // RangeError: Invalid array length

修改数组的长度时:

将 length 设置为小于当前长度的值,多余的元素会被删除

将 length 设置为大于当前长度的值将会创建一个稀疏数组

当添加的元素超过当前长度时,数组会自动更新 length 属性。
如果 length 属性设置为不可写,则数组将无法更新它。会导致错误。

// 修改数组的长度
const list = [1, 2, 3, 4, 5, 6]
// 将 length 设置为小于当前长度的值,多余的元素会被删除
list.length = 0
console.log(list) //[]
// 将 length 设置为大于当前长度的值将会创建一个稀疏数组
list.length = 8
console.log(list) //[empty × 8]
// 当添加的元素超过当前长度时,数组会自动更新 length 属性。
// 如果 length 属性设置为不可写,则数组将无法更新它。会导致错误。
const numbers = [1, 2, 3, 4, 5]
Object.defineProperty(numbers, 'length', { writable: false })
numbers[5] = 6 // TypeError: Cannot assign to read only property 'length' of object '[object Array]'
numbers.push(5) // // TypeError: Cannot assign to read only property 'length' of object '[object Array]'

Array.of()

Array.of() 创建一个包含所有传入参数的数组,而不考虑参数的数量与类型。

Array.of() 和 Array() 构造函数之间的区别在于对单个参数的处理:Array.of(7) 创建一个具有单个元素 7 的数组,而 Array(7) 创建一个 length 为 7 的空数组。

of() 方法可以在任何接受单个参数表示新数组长度的构造函数上调用。

当 this 值不是构造函数时,返回一个普通的 Array 对象。

// Array.of()
let arr = Array.of(7)
console.log(arr) // [7]
let arr1 = Array(7)
console.log(arr1) //[empty × 7]
let arr2 = Array.of(1, undefined, '3')
console.log(arr2) // [1, undefined, '3']

// of() 方法可以在任何接受单个参数表示新数组长度的构造函数上调用。
function NotArray(len) {
  console.log('NotArray called with length', len)
}
let res = Array.of.call(NotArray, 1, 2, 3) //NotArray called with length 3
console.log(res) //NotArray {0: 1, 1: 2, 2: 3, length: 3}

let res1 = Array.of.call(Object)
console.log(res1) //Number {0, length: 0}

let res2 = Array.of.call({}, 12, 11)
console.log(res2) //[12, 11]

let res3 = Array.of.call({ 1: 2 }, 12, 11)
console.log(res3) //[12, 11]

// Array.of() 自定义方法实现
function ArrayOf() {
  return [].slice.call(arguments)
}

let res4 = ArrayOf.call({ 1: 2 }, 12, 11)
console.log(res4) //[12, 11]
let res5 = ArrayOf.call(Object)
console.log(res5) //[]

Array.prototypeSymbol.iterator

Symbol.iterator 为每一个对象定义了默认的迭代器。该迭代器可以被 for…of 循环使用。

当需要对一个对象进行迭代时(比如开始用于一个 for…of 循环中),它的 @@iterator 方法都会在不传参情况下被调用,返回的迭代器用于获取要迭代的值。

一些内置类型拥有默认的迭代器行为,其他类型(如 Object)则没有。拥有默认的 @@iterator 方法的内置类型是:

使用 for…of 循环进行迭代
// Array.prototype[Symbol.iterator]()
// 使用 for...of 循环进行迭代
let arr = [1, 2, 3, 4]

for (const item of arr) {
  console.log(item)
  // 1
  // 2
  // 3
  // 4
}
手动执行迭代器

手动调用返回迭代器对象的 next() 方法,以实现对迭代过程的最大控制。

// 手动执行迭代器
// 手动调用返回迭代器对象的 next() 方法,以实现对迭代过程的最大控制。
//调用返回迭代器对象的 next() 方法,返回一个对象,value是值,done是用来判断之后是否还有值的标志

const arr = ['a', 'b', 'c']
const arrIter = arr[Symbol.iterator]()
console.log(arrIter.next()) // {value: 'a', done: false}
console.log(arrIter.next().value) // b
console.log(arrIter.next().value) // c
console.log(arrIter.next().value) // undefined
console.log(arrIter.next()) // {value: undefined, done: true}
自定义迭代器

生成器的函数的function后面有个*,函数中存在yield 关键字,在函数中,yield表达式就是暂停标志.

通过 生成器 给对象添加 Symbol.iterator

var people = {
  name: 'test',
  sex: 'male',
  hobbies: ['ball', 'paint', 'sing']
}
function* itertorObj() {
  let keys = Object.keys(this)
  for (let key of keys) {
    yield [key, this[key]]
  }
}
people[Symbol.iterator] = itertorObj.bind(people)

for (let [key, value] of people) {
  console.log(key, value)
  // name test
  // sex male
  // hobbies  ['ball', 'paint', 'sing']
}

//通过给对象添加[Symbol.iterator]属性方法,返回next()方法添加迭代器
var people1 = {
  name: 'test',
  sex: 'male',
  hobbies: ['ball', 'paint', 'sing'],
  [Symbol.iterator]() {
    let index = 0
    return {
      next: () => {
        let keys = Object.keys(this)
        return index >= keys.length ? { done: true } : { value: this[keys[index++]], done: false }
      }
    }
  }
}
for (let h of people1) {
  console.log(h)
  //  test
  //  male
  // ['ball', 'paint', 'sing']
}
for of 原理

for of 的原理就是调用迭代对象Symbol.iterator属性指向的函数,得到一个迭代器,利用迭代器的next()实现

function myforof(iteratorObj, cb) {
  // 调用可迭代对象的iterator方法,返回一个迭代器
  const iterator = iteratorObj[Symbol.iterator]()
  let res = iterator.next()
  while (!res.done) {
    cb(res.value)
    res = iterator.next()
  }
}
const arr = [1, 2, 3]
myforof(arr, function (val) {
  console.log(val)
  // 1
  // 2
  // 3
})

Array.prototype.at()

at() 方法接收一个整数值并返回该索引对应的元素,允许正数和负数。负整数从数组中的最后一个元素开始倒数。

在传递非负数时,at() 方法等价于括号表示法。例如,array[0] 和 array.at(0) 均返回第一个元素。

当 index < 0 时,该方法将访问索引 index + array.length。

at() 方法是通用的。其仅期望 this 具有 length 属性和以整数为键的属性。

// Array.prototype.at()
const arr = ['apple', 'banana', 'pear']

// 返回数组的最后一个值
console.log(arr.at(-1)) //pear

// 返回数组的第一个值
console.log(arr.at(0)) //apple

// 在非数组对象上调用 at()
// at() 方法读取 this 的 length 属性并计算需要访问的索引。

const arrayLike = {
  length: 3,
  0: 'a',
  1: 'b',
  a: 'aaa'
}
console.log(Array.prototype.at.call(arrayLike, -2)) // b
console.log(Array.prototype.at.call(arrayLike, -1)) // undefined

Array.prototype.concat()

concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。

concat 方法创建一个新数组。该数组将首先由调用它的对象中的元素填充。然后,对于每个参数,它的值将被连接到数组中——对于普通对象或基元,参数本身将成为最终数组的一个元素;对于属性Symbol.isConcatSpreadable设置为真的数组或类数组对象,参数的每个元素都将是独立地添加到最终数组中。concat 方法不会递归到嵌套数组参数中。

concat() 方法是一种复制方法。它不会更改 this 或作为参数提供的任何数组,而是返回包含与原始数组中的元素相同的元素的浅拷贝。

如果任何源数组是稀疏数组,concat() 方法会保留空槽。

将两个数组合并为一个新数组
const letters = ['a', 'b', 'c']
const nums = [1, 2, 3]

const alphaNumeric = letters.concat(nums)
console.log(alphaNumeric)
// ['a', 'b', 'c', 1, 2, 3]
将三个数组合并为一个新数组
const num1 = [1, 2, 3]
const num2 = [4, 5, 6]
const num3 = [7, 8, 9]

const numbers = num1.concat(num2, num3)
console.log(numbers)
// [1, 2, 3, 4, 5, 6, 7, 8, 9]
将三个值连接到数组
const value1 = ['a', 'b', 'c']
const value2 = 2
const value3 = ['a1', 'b2']

const values = value1.concat(value2, value3)
console.log(values)
//  ['a', 'b', 'c', 2, 'a1', 'b2']
合并数组并保留引用,concat()是一种浅拷贝的复制合并方法
const merge1 = [[1]]
const merge2 = [2, [3]]
const merges = merge1.concat(merge2)
console.log(merges)
// [[1], 2, [3]]

// 修改 merge1 的第一个元素
merge1[0].push(4)

console.log(merges)
// [[1, 4], 2, [3]]
使用 Symbol.isConcatSpreadable 合并类数组对象

concat 默认情况下不会将类数组对象视作数组——仅在 Symbol.isConcatSpreadable 被设置为真值(例如,true)时才会将类数组对象视作数组。

const obj1 = { 0: 1, 1: 2, 2: 3, length: 3 }
const obj2 = { 0: 1, 1: 2, 2: 3, length: 3, [Symbol.isConcatSpreadable]: true }
console.log([0].concat(obj1, obj2))
// [ 0, { '0': 1, '1': 2, '2': 3, length: 3 }, 1, 2, 3 ]
在稀疏数组上使用 concat()

如果任何源数组是稀疏的,则结果数组也将是稀疏的:

console.log([1, , 3].concat([4, 5])) //  [1, empty, 3, 4, 5]
console.log([1, 2].concat([3, , 5])) // [1, 2, 3, empty, 5]
在非数组对象上调用 concat()

如果 this 值不是数组,它会被转换为一个对象,然后以与 concat() 的参数相同的方式处理。在这种情况下,返回值始终是一个普通的新数组。

console.log(Array.prototype.concat.call({}, 1, 2, 3)) // [{}, 1, 2, 3]
console.log(Array.prototype.concat.call(1, 2, 3)) // [ [Number: 1], 2, 3 ]
const arrayLike = { [Symbol.isConcatSpreadable]: true, length: 2, 0: 1, 1: 2 }
console.log(Array.prototype.concat.call(arrayLike, 3, 4)) // [1, 2, 3, 4]

Array.prototype.constructor

constructor 是数组的构建函数的属性,constructor()是数组的引用。

所有的数组实例都继承了这个属性constructor,它的值就是 Array,表明了所有的数组都是由 Array 构造出来的。

Array.prototype.constructor() 属性返回对创建此对象的数组函数的引用。

constructor()是数组的引用,并不是指向构造函数constructor,一个带括号一个不带。

所有数组的constructor()引用值都是 “[]”。

let arr = [1, 2]
let res = arr.constructor()
console.log(res)
// []
let res1 = arr.constructor
console.log(res1)
// ƒ Array() { [native code] }

Array.prototype.copyWithin()

copyWithin() 方法浅复制数组的一部分到同一数组中的另一个位置,并返回它,改变原数组,不会改变原数组的长度。

copyWithin() 是修改方法。它不会改变 this 指向的对象(数组或类数组)的长度,但会更改其的内容,并在必要时创建新属性或删除现有属性。

copyWithin() 方法保留空槽。如果要复制的区域是稀疏的,则原来的空槽会被删除并被替换为拷贝的空槽。

copyWithin() 方法是通用方法。它只期望 this 值具有 length 属性和整数键属性。虽然字符串也是类似数组的,但这种方法不适合应用于它们,因为字符串是不可变的。

语法规则

copyWithin(target, start, end)

  • target :序列开始替换的目标位置,以 0 为起始的下标表示,且将被转换为整数。
  • start :可选,要复制的元素序列的起始位置,以 0 为起始的下标表示,且将被转换为整数。
  • end :可选,要复制的元素序列的结束位置,以 0 为起始的下标表示,且将被转换为整数。copyWithin 将会拷贝到该位置,但不包括 end 这个位置的元素。

复制只会持续到 array.length 结束(换句话说,copyWithin() 永远不会扩展数组)

使用 copyWithin()
let arr = [1, 2, 3, 4, 5]
let res = arr.copyWithin(3, 0)
console.log(arr) //[1, 2, 3, 1, 2]
console.log(res) //[1, 2, 3, 1, 2]

// 不传值时无变化
let res1 = arr.copyWithin()
console.log(res1) //[1, 2, 3, 1, 2]

//负索引将从数组末尾开始计数 —— index + array.length
let arr2 = [1, 2, 3, 4, 5]
let res2 = arr2.copyWithin(-2)
console.log(res2) //[1, 2, 3, 1, 2]

// 将数组后一位复制到索引为1的位置上
let arr3 = [1, 2, 3, 4, 5]
let res3 = arr3.copyWithin(1, -1)
console.log(res3) //[1, 5, 3, 4, 5]

// 要复制的元素序列的结束位置要大于开始位置,否则不复制
let arr4 = [1, 2, 3, 4, 5]
let res4 = arr4.copyWithin(1, -2, 3)
console.log(res4) //[1, 2, 3, 4, 5]

let arr5 = [1, 2, 3, 4, 5]
let res5 = arr5.copyWithin(1, -2, 2)
console.log(res5) //[1, 2, 3, 4, 5]

let arr6 = [1, 2, 3, 4, 5]
let res6 = arr6.copyWithin(1, 2, 3)
console.log(res6) //[1, 3, 3, 4, 5]
在稀疏数组上使用 copyWithin()

copyWithin() 将保留空插槽。

let arr7 = [1, 2, , 4, 5]
let res7 = arr7.copyWithin(1, 2, 3)
console.log(res7) //[1, empty × 2, 4, 5]
在非数组对象上调用 copyWithin()

copyWithin() 方法读取 this 的 length 属性,然后操作所涉及的整数索引。

const arrayLike = {
  length: 5,
  3: 1
}
console.log(Array.prototype.copyWithin.call(arrayLike, 0, 3))
// {0: 1, 3: 1, length: 5}
console.log(Array.prototype.copyWithin.call(arrayLike, 3, 1))
// {0: 1, length: 5}
// '3' 属性被删除,因为在复制的源中是一个空槽

Array.prototype.entries()

entries() 方法返回一个新的数组迭代器 (en-US)对象,该对象包含数组中每个索引的键/值对。

当在稀疏数组上使用时,entries() 方法迭代空槽,就像它们的值为 undefined 一样。

entries() 方法是通用的。它只要求 this 值具有 length 属性和以整数为键的属性。

entries(),keys()和 values() —— 用于遍历数组。它们都返回一个遍历器对象,可以用for…of循环进行遍历

区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历

entries()如果不使用for…of循环,可以手动调用遍历器对象的next方法,进行遍历。

迭代索引和元素
使用 for…of 循环
const a = ['a', 'b', 'c']

for (const [index, element] of a.entries()) {
  console.log(index, element)
}
// 0 'a'
// 1 'b'
// 2 'c'
调用遍历器对象的next方法
let letter = ['a', 'b', 'c']
let entries = letter.entries()
console.log(entries.next().value) // [0, 'a']
console.log(entries.next().value) // [1, 'b']
console.log(entries.next().value) // [2, 'c']
迭代稀疏数组

entries() 将访问空槽,就像它们是 undefined 一样。

for (const element of [, 'a'].entries()) {
  console.log(element)
}
// [0, undefined]
// [1, 'a']
在非数组对象上调用 entries()

entries() 方法读取 this 的 length 属性,然后访问每个整数索引。

const arrayLike = {
  length: 3,
  0: 'a',
  1: 'b',
  2: 'c'
}
for (const entry of Array.prototype.entries.call(arrayLike)) {
  console.log(entry)
}
// [ 0, 'a' ]
// [ 1, 'b' ]
// [ 2, 'c' ]

Array.prototype.every()

every() 方法判断一个数组内的所有元素是否都能通过指定函数的测试。它返回一个布尔值。

every() 方法是一个迭代方法。它为数组中的每个元素调用一次指定的 callbackFn 函数,直到 callbackFn 返回一个假值。如果找到这样的元素,every() 方法将会立即返回 false 并停止遍历数组。否则,如果 callbackFn 为每个元素返回一个真值,every() 就会返回 true。

对于空数组,它只返回 true。(这种情况属于无条件正确,因为空集的所有元素都符合给定的条件。)

callbackFn 仅针对已分配值的数组索引调用。它不会为稀疏数组中的空槽调用。

every() 不会改变调用它的数组,但指定的 callbackFn 函数可以。但是请注意,数组的长度是在第一次调用 callbackFn 之前保存的。所以:

  • 当开始调用 every() 时,callbackFn 将不会访问超出数组初始长度的任何元素。
  • 对已访问索引的更改不会导致再次在这些元素上调用 callbackFn。
  • 如果数组中一个现有的、尚未访问的元素被 callbackFn 更改,则它传递给 callbackFn 的值将是该元素被修改后的值。被删除的元素则不会被访问。

every() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。

使用every()
// 数组中的所有元素是否都大于 10
function isBigEnough(element) {
  return element >= 10
}
let res = [12, 5, 8, 130, 44].every(isBigEnough)
console.log(res) //false
let res1 = [12, 54, 18, 130, 44].every(isBigEnough)
console.log(res1) //true

// 检查一个数组是否是另一个数组的子集
```bash
const isSubset = (array1, array2) => array2.every(element => array1.includes(element))
console.log(isSubset([1, 2, 3, 4, 5, 6, 7], [5, 7, 6])) // true
console.log(isSubset([1, 2, 3, 4, 5, 6, 7], [5, 8, 7])) // false
影响初始数组(修改、添加和删除)
// --------------
//   修 改 元 素
// --------------
let arr = [1, 2, 3, 4]
arr.every((elem, index, arr) => {
  arr[index + 1]--
  console.log(`[${arr}][${index}] -> ${elem}`)
  return elem < 2
})

// 循环会迭代 3 次
// 没有修改的情况下只会迭代 2 次
//
// 第 1 次迭代:[1,1,3,4][0] -> 1
// 第 2 次迭代:[1,1,2,4][1] -> 1
// 第 3 次迭代:[1,1,2,3][2] -> 2

// --------------
//   添 加 元 素
// --------------
arr = [1, 2, 3]
arr.every((elem, index, arr) => {
  arr.push('new')
  console.log(`[${arr}][${index}] -> ${elem}`)
  return elem < 4
})

// 循环迭代 3 次,即使在添加新元素之后
//
// 第 1 次迭代:[1, 2, 3, new][0] -> 1
// 第 2 次迭代:[1, 2, 3, new, new][1] -> 2
// 第 3 次迭代:[1, 2, 3, new, new, new][2] -> 3

// --------------
//   删 除 元 素
// --------------
arr = [1, 2, 3, 4]
arr.every((elem, index, arr) => {
  arr.pop()
  console.log(`[${arr}][${index}] -> ${elem}`)
  return elem < 4
})

// 循环仅迭代 2 次
// 因为剩余的元素被 `pop()` 删除
//
// 第 1 次迭代:[1,2,3][0] -> 1
// 第 2 次迭代:[1,2][1] -> 2
在非数组对象上调用 every()

every() 方法读取 this 的 length 属性,然后访问每个整数索引,直到到达末尾或 callbackFn 返回 false。

const arrayLike = {
  length: 3,
  0: 'a',
  1: 'b',
  2: 'c'
}
console.log(Array.prototype.every.call(arrayLike, x => typeof x === 'string')) // true

Array.prototype.fill()

fill() 方法用一个固定值填充一个数组中从起始索引(默认为 0)到终止索引(默认为 array.length)内的全部元素。它返回修改后的数组。

语法规则

fill(value, start, end)

  • value : 用来填充数组元素的值。注意所有数组中的元素都将是这个确定的值:如果 value 是个对象,那么数组的每一项都会引用这个元素。
  • start : 可选,基于零的索引,从此开始填充,转换为整数。
  • end : 可选,基于零的索引,在此结束填充,转换为整数。fill() 填充到但不包含 end 索引。
使用 fill
console.log([1, 2, 3].fill(4))
// [4, 4, 4]
console.log([1, 2, 3].fill(4, 1))
// [1, 4, 4]
console.log([1, 2, 3].fill(4, 1, 2))
// [1, 4, 3]
console.log([1, 2, 3].fill(4, 1, 1))
// [1, 2, 3]
console.log([1, 2, 3].fill(4, 3, 3))
// [1, 2, 3]
console.log([1, 2, 3].fill(4, -3, -2))
// [4, 2, 3]
console.log([1, 2, 3].fill(4, NaN, NaN))
// [1, 2, 3]
console.log([1, 2, 3].fill(4, 3, 5))
// [1, 2, 3]
console.log(Array(3).fill(4))
// [4, 4, 4]

// 一个简单的对象,被数组的每个空槽所引用
const arr = Array(3).fill({})
console.log(arr)
// [{}, {}, {}]
arr[0].hi = 'hi'
console.log(arr)
// [{ hi: "hi" }, { hi: "hi" }, { hi: "hi" }]
在非数组对象上调用 fill()
const arrayLike = { length: 2 }
console.log(Array.prototype.fill.call(arrayLike, 1))
// { '0': 1, '1': 1, length: 2 }

Array.prototype.filter()

filter() 是一次过滤,用于把数组的某些元素过滤掉,然后返回剩下的元素。

它与map()方法不一样,map()是一次统一映射,不会改变数组长度。filter()是一次过滤,会挑选满足条件的,能改变数组长度。

filter() 方法是一个迭代方法。它为数组中的每个元素调用提供的 callbackFn 函数一次,并构造一个由所有返回真值的元素值组成的新数组。未通过 callbackFn 测试的数组元素不会包含在新数组中。

callbackFn 仅对已分配值的数组索引调用。它不会对稀疏数组中的空槽调用。

filter() 方法是一个复制方法。它不会改变 this,而是返回一个包含与原始数组相同的元素(其中某些元素已被过滤掉)的浅拷贝。但是,作为 callbackFn 的函数可以更改数组。请注意,在第一次调用 callbackFn 之前,数组的长度已经被保存。因此:

  • 当开始调用 filter() 时,callbackFn 将不会访问超出数组初始长度的任何元素。
  • 对已访问索引的更改不会导致再次在这些元素上调用 callbackFn。
  • 如果数组中一个现有的、尚未访问的元素被 callbackFn 更改,则它传递给 callbackFn 的值将是该元素被修改后的值。被删除的元素则不会被访问。

filter() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。

使用filter()

filter()接收的回调函数,其实可以有多个参数。通常我们仅使用第一个参数,表示Array的某个元素。回调函数还可以接收另外两个参数,表示元素的位置和数组本身:

let list = ['A', 'B', 'C']
let res = list.filter(function (element, index, self) {
  console.log(element) // 依次打印'A', 'B', 'C'
  console.log(index) // 依次打印0, 1, 2
  console.log(self) // self 就是变量 list
  return true
})
筛选排除所有较小的值
function isBigEnough(value) {
  return value >= 10
}
let arr = [12, 5, 8, 130, 44]
const filtered = arr.filter(isBigEnough)
console.log(arr)
// [12, 5, 8, 130, 44]
console.log(filtered)
// [12, 130, 44]
找出数组中所有的素数
const array = [-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
function isPrime(num) {
  for (let i = 2; num > i; i++) {
    if (num % i === 0) {
      return false
    }
  }
  return num > 1
}
console.log(array.filter(isPrime))
// [2, 3, 5, 7, 11, 13]
过滤 JSON 中的无效条目
const arr1 = [
  {
    id: 15
  },
  {
    id: -1
  },
  {
    id: 0
  },
  {
    id: 3
  },
  {
    id: 12.2
  },
  {},
  {
    id: null
  },
  {
    id: NaN
  },
  {
    id: 'undefined'
  }
]
let invalidEntries = 0

function filterByID(item) {
  if (Number.isFinite(item.id) && item.id !== 0) {
    return true
  }
  invalidEntries++
  return false
}

const arrByID = arr1.filter(filterByID)
console.log(arrByID)
// [{ id: 15 }, { id: -1 }, { id: 3 }, { id: 12.2 }]
console.log('无效条目数量 = ', invalidEntries)
// 无效条目数量 = 5
在数组中搜索
const fruits = ['apple', 'banana', 'grapes', 'mango', 'orange']

// 根据搜索条件(查询)筛选数组项
function filterItems(arr, query) {
  return arr.filter(el => el.toLowerCase().includes(query.toLowerCase()))
}

console.log(filterItems(fruits, 'ap'))
// ['apple', 'grapes']
console.log(filterItems(fruits, 'an'))
// ['banana', 'mango', 'orange']
去除Array的重复元素
let arr2 = ['apple', 'strawberry', 'banana', 'pear', 'apple', 'orange', 'orange', 'strawberry']
let filtered2 = arr2.filter(function (element, index, self) {
  return self.indexOf(element) === index
})
console.log(arr2)
// ['apple', 'strawberry', 'banana', 'pear', 'apple', 'orange', 'orange', 'strawberry']
console.log(filtered2)
//  ['apple', 'strawberry', 'banana', 'pear', 'orange']
在稀疏数组上使用 filter()

filter() 将跳过空槽。

console.log([1, , undefined].filter(x => x === undefined))
// [undefined]
console.log([1, , undefined].filter(x => x !== 2))
// [1, undefined]
影响初始数组(修改、追加和删除)
// 修改每个单词
let words = ['spray', 'limit', 'exuberant', 'destruction', 'elite', 'present']

const modifiedWords = words.filter((word, index, arr) => {
  arr[index + 1] += ' extra'
  return word.length < 6
})

console.log(modifiedWords)
// 注意,在长度为6以下有三个单词,但是由于它们已经被修改,所以返回一个单词
// ["spray"]

// 添加新单词
words = ['spray', 'limit', 'exuberant', 'destruction', 'elite', 'present']
const appendedWords = words.filter((word, index, arr) => {
  arr.push('new')
  return word.length < 6
})

console.log(appendedWords)
// 只有三个符合条件,即使 `words`本身现在有更多字符长度小于 6 的单词
// ["spray" ,"limit" ,"elite"]

// 删除单词
words = ['spray', 'limit', 'exuberant', 'destruction', 'elite', 'present']
const deleteWords = words.filter((word, index, arr) => {
  arr.pop()
  return word.length < 6
})

console.log(deleteWords)
// 注意,'elite' 也没有获得,因为它在过滤器达到之前就已经从 'words' 弹出了
// ["spray" ,"limit"]
自定义filter方法
Array.prototype.filterCustom = function (callbackfn, thisArg) {
  if (typeof callbackfn !== 'function') {
    throw new TypeError(callbackfn + 'must be a function')
  }
  const arr = this
  const thisValue = thisArg || this
  const returnValue = []
  for (let i = 0; i < arr.length; i++) {
    if (callbackfn.call(thisValue, arr[i], i, arr)) {
      returnValue.push(arr[i])
    }
  }
  return returnValue
}
let a = [1, 2, 3].filterCustom(item => item > 1)
console.log(a) //[ 2, 3 ]

Array.prototype.find()

find() 返回数组中满足条件的第一个元素的值。否则返回 undefined。

查找方法
  • 如果需要在数组中找到对应元素的索引,请使用 findIndex()。
  • 如果需要查找某个值的索引,请使用 Array.prototype.indexOf()。(它类似于 findIndex(),但只是检查每个元素是否与值相等,而不是使用测试函数。)
  • 如果需要查找数组中是否存在某个值,请使用 Array.prototype.includes()。同样,它检查每个元素是否与值相等,而不是使用测试函数。
  • 如果需要查找是否有元素满足所提供的测试函数,请使用 Array.prototype.some()。
find()方法的描述

find() 方法是一个迭代方法。它按索引升序顺序为数组中的每个元素调用提供的 callbackFn 函数,直到 callbackFn 返回一个真值。然后 find() 返回该元素并停止迭代数组。如果 callbackFn 从未返回真值,则 find() 返回 undefined。

callbackFn 被调用来处理数组的每一个索引,而不仅仅是那些有值的索引。在稀疏数组中,未赋值的空槽与 undefined 表现相同。

find() 不会改变被调用的数组,但是提供给 callbackFn 的函数可能会改变它。但需要注意的是,在第一次调用 callbackFn 之前,数组的长度会被保存。因此:

  • 当调用 find() 时,callbackFn 不会访问超出数组初始长度的任何元素。
  • 对已经访问过的索引的更改不会导致再次在这些元素上调用 callbackFn。
  • 如果 callbackFn 改变了数组中已存在但尚未被访问的元素,则传递给 callbackFn 的该元素的值将是该元素在被访问时的值。被删除的元素被视为 undefined。

find() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。

使用find()

// 使用find()

在对象数组中通过对象属性进行查找
const inventory = [
  { name: 'apples', quantity: 2 },
  { name: 'bananas', quantity: 0 },
  { name: 'cherries', quantity: 5 }
]
// 在对象数组中通过对象属性进行查找对象的key为name的值为cherries的对象
function isCherries(fruit) {
  return fruit.name === 'cherries'
}
console.log(inventory.find(isCherries))
// { name: 'cherries', quantity: 5 }
使用箭头函数和解构赋值
const inventory1 = [
  { name: 'apples', quantity: 2 },
  { name: 'bananas', quantity: 0 },
  { name: 'cherries', quantity: 5 }
]
const result = inventory1.find(({ name }) => name === 'cherries')
console.log(result)
// { name: 'cherries', quantity: 5 }
寻找数组中的第一个素数

如果找不到素数则返回 undefined

function isPrime(element) {
  let start = 2
  while (start <= Math.sqrt(element)) {
    if (element % start++ < 1) {
      return false
    }
  }
  return element > 1
}

console.log([4, 6, 8, 12].find(isPrime))
// undefined
console.log([4, 5, 8, 12].find(isPrime))
// 5
在稀疏数组上使用 find()

在稀疏数组中,空槽会被访问的,并被视为 undefined。

// 声明一个在索引 2、3 和 4 处没有元素的数组
const array = [0, 1, , , , 5, 6]

// 将会打印所有索引,而不仅仅是那些有值的非空槽
array.find((value, index) => {
  console.log(`访问索引 ${index},值为 ${value}`)
})
// 访问索引 0,值为 0
// 访问索引 1,值为 1
// 访问索引 2,值为 undefined
// 访问索引 3,值为 undefined
// 访问索引 4,值为 undefined
// 访问索引 5,值为 5
// 访问索引 6,值为 6

// 打印所有索引,包括已删除的
```bash
array.find((value, index) => {
  // 在第一次迭代时删除元素5
  if (index === 0) {
    console.log(`删除 array[5] 的值 ${array[5]}`)
    delete array[5]
  }
  // 即使删除了,元素5仍然被访问
  console.log(`访问索引 ${index},值为 ${value}`)
})
// 删除 array[5] 的值 5
// 访问索引 0,值为 0
// 访问索引 1,值为 1
// 访问索引 2,值为 undefined
// 访问索引 3,值为 undefined
// 访问索引 4,值为 undefined
// 访问索引 5,值为 undefined
// 访问索引 6,值为 6
在非数组对象上调用 find()

find() 方法读取 this 的 length 属性,然后访问每个整数索引。

const arrayLike = {
  length: 3,
  0: 2,
  1: 7.3,
  2: 4
}
console.log(Array.prototype.find.call(arrayLike, x => !Number.isInteger(x)))
// 7.3

Array.prototype.findIndex()

findIndex() 返回数组中满足条件的第一个元素的索引。否则返回 -1。

findIndex() 是一种迭代方法。它按照索引升序依次遍历数组中的每个元素,并调用提供的 callbackFn 函数,直到 callbackFn 返回一个真值。然后 findIndex() 返回该元素的索引并停止遍历数组。如果 callbackFn 从未返回一个真值,则 findIndex() 返回 -1。

callbackFn 被调用来处理数组的每一个索引,而不仅仅是那些有值的索引。在稀疏数组中,未赋值的空槽与 undefined 表现相同。

findIndex() 不会改变被调用的数组,但是提供给 callbackFn 的函数可能会改变它。但需要注意的是,在第一次调用 callbackFn 之前,数组的长度会被保存。因此:

  • 当调用 findIndex() 时,callbackFn 不会访问超出数组初始长度的任何元素。
  • 对已经访问过的索引的更改不会导致再次在这些元素上调用 callbackFn。
  • 如果 callbackFn 改变了数组中已存在但尚未被访问的元素,则传递给 callbackFn 的该元素的值将是该元素在被访问时的值。被删除的元素被视为 undefined。

findIndex() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。

使用findIndex()
寻找数组中的首个素数的索引
返回数组中第一个素数的索引,如果没有素数则返回 -1。
function isPrime(element) {
  if (element % 2 === 0 || element < 2) {
    return false
  }
  for (let factor = 3; factor <= Math.sqrt(element); factor += 2) {
    if (element % factor === 0) {
      return false
    }
  }
  return true
}
console.log([4, 6, 8, 9, 12].findIndex(isPrime))
// -1
console.log([4, 6, 7, 9, 12].findIndex(isPrime))
// 2
在稀疏数组上使用 findIndex()

可以搜索稀疏数组中的 undefined 并来获取空槽的索引。

console.log([1, , 3].findIndex(x => x === undefined))
// 1
在非数组对象上调用 findIndex()

findIndex() 方法读取 this 的 length 属性,并访问每个整数索引。

const arrayLike = {
  length: 3,
  0: 2,
  1: 7.3,
  2: 4
}
console.log(Array.prototype.findIndex.call(arrayLike, x => !Number.isInteger(x)))
// 1

Array.prototype.findLast()

findLast() 相反的顺序迭代数组,返回数组中满足条件的第一个元素的值。否则返回 undefined。

findLast() 是一个迭代方法。该方法对数组每一个元素按降序(索引从大到小)执行 callbackFn 函数,直到 callbackFn 返回一个真值。然后 findLast() 返回该元素的值并停止遍历数组。如果 callbackFn 没有返回一个真值,则 findLast() 返回 undefined。

callbackFn 会被数组中的每个元素调用,而不仅仅是那些被赋值的元素。对于稀疏数组来说,空槽行为和 undefined 相同。

findLast() 方法不会改变调用它的数组,但是提供的 callbackFn 可以。但是请注意,数组的长度是在第一次调用 callbackFn 之前保存的。因此:

  • callbackFn 不会访问在调用 findLast() 开始后才添加到数组中的任何元素。
  • 给已访问过的索引重新赋值将不会被 callbackFn 重新访问。
  • 如果 callbackFn 更改了数组中现有的、尚未访问的元素,则其传递给 callbackFn 的值将是 findLast() 访问该元素索引时的值。已删除的元素会被当做 undefined 来访问。

findLast() 方法是通用的。它只期望 this 值具有 length 属性和整数键的属性。

使用 findLast()
查找与元素属性匹配的数组中的最后一个对象
const inventory = [
  { name: 'apples', quantity: 2 },
  { name: 'bananas', quantity: 0 },
  { name: 'fish', quantity: 1 },
  { name: 'cherries', quantity: 5 }
]

// 真实的库存低时返回 true
function isNotEnough(item) {
  return item.quantity < 2
}

console.log(inventory.findLast(isNotEnough))
// { name: "fish", quantity: 1 }
使用箭头函数和解构
const inventory1 = [
  { name: 'apples', quantity: 2 },
  { name: 'bananas', quantity: 0 },
  { name: 'fish', quantity: 1 },
  { name: 'cherries', quantity: 5 }
]

const result = inventory1.findLast(({ quantity }) => quantity < 2)

console.log(result)
// { name: "fish", quantity: 1 }
查找数组中的最后一个素数

查找数组中的最后一个素数元素(如果没有素数,则返回 undefined)

function isPrime(element) {
  if (element % 2 === 0 || element < 2) {
    return false
  }
  for (let factor = 3; factor <= Math.sqrt(element); factor += 2) {
    if (element % factor === 0) {
      return false
    }
  }
  return true
}

console.log([4, 6, 8, 12].findLast(isPrime))
// undefined
console.log([4, 5, 7, 8, 9, 11, 12].findLast(isPrime))
// 11
在稀疏数组上使用 findLast()

稀疏数组中的空槽被访问,并被视为 undefined。

// 声明一个在索引 2、3 和 4 处没有元素的数组
const array = [0, 1, , , , 5, 6]

// 显示所有的索引(不只包括那些被赋值的)
array.findLast((value, index) => {
  console.log(`访问索引 ${index},值为 ${value}`)
})
// 访问索引 6,值为 6
// 访问索引 5,值为 5
// 访问索引 4,值为 undefined
// 访问索引 3,值为 undefined
// 访问索引 2,值为 undefined
// 访问索引 1,值为 1
// 访问索引 0,值为 0

// 显示所有的索引(包括已被删除的)
array.findLast((value, index) => {
  // 在第一次迭代时删除值为 5 的元素
  if (index === 6) {
    console.log(`删除值为 array[5],其值为 ${array[5]}`)
    delete array[5]
  }
  // 元素 5 在被删除后,仍会被访问
  console.log(`访问索引 ${index},值为 ${value}`)
})
// 删除值为 array[5],其值为 6
// 访问索引 6,值为 6
// 访问索引 5,值为 undefined
// 访问索引 4,值为 undefined
// 访问索引 3,值为 undefined
// 访问索引 2,值为 undefined
// 访问索引 1,值为 1
// 访问索引 0,值为 0
在非数组对象上调用 findLast()

findLast() 方法读取 this 的 length 属性,然后访问每个整数索引。

const arrayLike = {
  length: 3,
  0: 2,
  1: 7.3,
  2: 4
}
console.log(Array.prototype.findLast.call(arrayLike, x => Number.isInteger(x)))
// 4

Array.prototype.findLastIndex()

findLastIndex() 相反的顺序迭代数组,返回数组中满足条件的第一个元素的索引。否则返回 -1。

findLastIndex() 方法是一个迭代方法。它为数组中的每个元素按索引降序调用一次提供的 callbackFn 函数,直到 callbackFn 返回一个真值。然后 findLastIndex() 返回元素的索引并且停止遍历数组。如果 callbackFn 没有返回一个真值,则 findLastIndex() 返回 -1。

callbackFn 会为数组中的每个元素调用,而不仅仅是那些被赋值的元素,这意味着对于稀疏数组来说,空槽的行为和 undefined 相同。

findLastIndex() 方法不会改变调用它的数组,但是提供的 callbackFn 可以。findLastIndex() 处理的元素是在第一次调用 callbackFn 之前设置的。因此:

  • callbackFn 不会访问在调用 findLastIndex() 开始后才添加到数组中的任何元素。
  • 对已访问索引的更改不会导致对它们再次调用 callbackFn 函数。
  • 如果 callbackFn 更改了数组中现有的、尚未访问的元素,它传递给callbackFn 的值将是该元素被访问时的值。已删除元素被当作未定义的来访问。

findLastIndex() 方法是通用的。它只期望 this 值具有 length 属性和整型键属性。

使用 findLastIndex()
查找数组中最后一个素数的索引

返回数组中作为素数的最后一个元素的索引,如果没有素数,则返回 -1。

function isPrime(element) {
  if (element % 2 === 0 || element < 2) {
    return false
  }
  for (let factor = 3; factor <= Math.sqrt(element); factor += 2) {
    if (element % factor === 0) {
      return false
    }
  }
  return true
}

console.log([4, 6, 8, 12].findLast(isPrime))
// undefined
console.log([4, 5, 7, 8, 9, 11, 12].findLast(isPrime))
// 11
在稀疏数组上使用 findLastIndex()

在稀疏数组中搜索 undefined 并获得空槽的索引。

console.log([1, , 3].findLastIndex(x => x === undefined))
// 1
在非数组对象上调用 findLastIndex()

findLastIndex() 方法读取 this 的 length 属性,然后访问每个整数索引。

const arrayLike = {
  length: 3,
  0: 2,
  1: 7.3,
  2: 4
}
console.log(Array.prototype.findLastIndex.call(arrayLike, x => Number.isInteger(x)))
// 2

Array.prototype.flat()

flat() 扁平化数组,按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。

该方法返回一个新数组,对原数据没有影响。

flat() 方法属于复制方法。它不会改变 this 数组,而是返回一个浅拷贝,该浅拷贝包含了原始数组中相同的元素。

如果待展开的数组是稀疏的,flat() 方法会忽略其中的空槽。例如,如果 depth 是 1,那么根数组和第一层嵌套数组中的空槽都会被忽略,但在更深的嵌套数组中的空槽则会与这些数组一起保留。

flat() 方法是通用的。它只需要 this 值具有 length 属性和整数键属性即可。但是,如果要展开元素,则它们必须是数组。

使用 flat()

展平嵌套数组

// 可指定要提取嵌套数组的结构深度,默认值为 1
const arr1 = [1, 2, [3, 4]]
console.log(arr1.flat())
// [1, 2, 3, 4]

const arr2 = [1, 2, [3, 4, [5, 6]]]
console.log(arr2.flat())
// [1, 2, 3, 4, [5, 6]]

const arr3 = [1, 2, [3, 4, [5, 6]]]
console.log(arr3.flat(2))
// [1, 2, 3, 4, 5, 6]

//使用 Infinity,可展开任意深度的嵌套数组
const arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]]
console.log(arr4.flat(Infinity))
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
在稀疏数组上使用 flat()
// flat() 方法删除数组中的空槽:
const arr5 = [1, 2, , 4, 5]
console.log(arr5.flat())
// [1, 2, 4, 5]

const array = [1, , 3, ['a', , 'c']]
console.log(array.flat())
// [ 1, 3, "a", "c" ]

const array2 = [1, , 3, ['a', , ['d', , 'e']]]
console.log(array2.flat())
// [ 1, 3, "a", ["d", empty, "e"] ]
console.log(array2.flat(2))
// [ 1, 3, "a", "d", "e"]
在非数组对象上调用 flat()

flat() 方法读取 this 的 length 属性,然后访问每个整数索引。如果元素不是数组,则直接将其附加到结果中。如果元素是数组,则根据 depth 参数进行展开操作。

const arrayLike = {
  length: 3,
  0: [1, 2],
  // 嵌套的类数组对象不会被展平
  1: { length: 2, 0: 3, 1: 4 },
  2: 5
}
console.log(Array.prototype.flat.call(arrayLike))
// [ 1, 2, { '0': 3, '1': 4, length: 2 }, 5 ]
自定义实现flat()方法
const flatCustom = (arr, level = 1) => {
  const res = []
  for (let i = 0; i < arr.length; i++) {
    if (Array.isArray(arr[i]) && level > 0) {
      res.push(...flatCustom(arr[i], level - 1))
    } else {
      res.push(arr[i])
    }
  }
  return res
}

const arr = [
  1,
  2,
  3,
  4,
  [1, 2, 3],
  [
    [1, 2],
    [1, 2, [1, 2]]
  ]
]
console.log(flatCustom(arr, 2))
// [ 1, 2, 3, 4, 1, 2, 3, 1, 2, 1, 2, [ 1, 2 ] ]
console.log(flatCustom(arr, Infinity))
// [ 1, 2, 3, 4, 1, 2, 3, 1, 2, 1, 2, 1, 2]

Array.prototype.flatMap()

flatMap() 扁平化数组,方法对数组中的每个元素应用给定的回调函数,然后将结果展开一级,返回一个新数组。它等价于在调用 map() 方法后再调用深度为 1 的 flat() 方法(arr.map(…args).flat()),但比分别调用这两个方法稍微更高效一些。

该方法返回一个新数组,不改变原数组。

flatMap() 方法是一个迭代方法。flatMap() 方法等同于调用 map(callbackFn, thisArg) 后再调用 flat(1)——对于每个元素,它都会生成一个新元素数组,并将生成的数组连接起来形成一个新数组。

flatMap() 方法是通用的。它只需要 this 值具有 length 属性和整数键属性即可。但是,如果要展开从回调函数 callbackFn 返回的值,则该值必须是数组。

使用 flatMap()

flatMap 就类似 map ,对每一个元素做自定义处理
回调函数 返回的内容应该是一个数组,然后把元素连接在一起。

const arr = [
  1,
  2,
  3,
  4,
  [1, 2, 3],
  [
    [1, 2],
    [1, 2, [1, 2]]
  ]
]

console.log(arr.flatMap(x => [5, 6]))
// [ 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6 ]
在稀疏数组上使用 flatMap()

callbackFn 不会被源数组中的空槽调用,因为 map() 不会调用,而 flat() 将忽略返回数组中的空槽。

console.log([1, 2, , 4, 5].flatMap(x => [x, x * 2]))
// [1, 2, 2, 4, 4, 8, 5, 10]
console.log([1, 2, 3, 4].flatMap(x => [, x * 2]))
// [2, 4, 6, 8]
在非数组对象上调用 flatMap()

flatMap() 方法读取 this 的 length 属性,然后访问每个整数索引。如果回调函数的返回值不是数组,则始终直接将其附加到结果数组的末尾。

const arrayLike = {
  length: 3,
  0: 1,
  1: 2,
  2: 3
}
console.log(Array.prototype.flatMap.call(arrayLike, x => [x, x * 2]))
// [1, 2, 2, 4, 3, 6]

// 回调函数返回的类数组对象不会被展平
console.log(
  Array.prototype.flatMap.call(arrayLike, x => ({
    length: 1,
    0: x
  }))
)
// [ { '0': 1, length: 1 }, { '0': 2, length: 1 }, { '0': 3, length: 1 } ]
自定义实现flat()方法
const flatMapCustom = (arr, cb) => {
  const res = []
  for (let i = 0; i < arr.length; i++) {
    res.push(...cb(arr[i], i))
  }
  return res
}
const arr1 = [
  1,
  2,
  3,
  4,
  [1, 2, 3],
  [
    [1, 2],
    [1, 2, [1, 2]]
  ]
]
console.log(arr1.flatMap(x => [x].flat(3)))
//  [1, 2, 3, 4, 1, 2, 3, 1, 2, 1, 2, 1, 2]
console.log(flatMapCustom(arr1, x => [x].flat(3)))
//  [1, 2, 3, 4, 1, 2, 3, 1, 2, 1, 2, 1, 2]

Array.prototype.forEach()

forEach() 方法按升序为数组中含有效值的每一项执行一次callback函数,那些已删除或者未初始化的项将被跳过(例如在稀疏数组上)。

forEach() 方法是一个迭代方法。它按索引升序地为数组中的每个元素调用一次提供的 callbackFn 函数。与 map() 不同,forEach() 总是返回 undefined,而且不能继续链式调用。其典型的用法是在链式调用的末尾执行某些操作。

callbackFn 仅对已赋值的数组索引调用。对于稀疏数组中的空槽,它不会被调用。

forEach() 不会改变其调用的数组,但是,作为 callbackFn 的函数可以更改数组。请注意,在第一次调用 callbackFn 之前,数组的长度已经被保存。因此:

  • 当调用 forEach() 时,callbackFn 不会访问超出数组初始长度的任何元素。
  • 已经访问过的索引的更改不会导致 callbackFn 再次调用它们。
  • 如果 callbackFn 更改了数组中已经存在但尚未访问的元素,则传递给 callbackFn 的值将是在访问该元素时的值。已经被删除的元素不会被访问。

forEach() 方法是通用的。它只期望 this 值具有 length 属性和整数键的属性。

除非抛出异常,否则没有办法停止或中断 forEach() 循环。如果有这样的需求,则不应该使用 forEach() 方法。

可以通过像 for、for…of 和 for…in 这样的循环语句来实现提前终止。当不需要进一步迭代时,诸如 every()、some()、find() 和 findIndex() 等数组方法也会立即停止迭代。

forEach() 期望的是一个同步函数,它不会等待 Promise 兑现。在使用 Promise(或异步函数)作为 forEach 回调时,请确保你意识到这一点可能带来的影响。

如果希望按顺序的或者并发的执行一系列操作,可以查看 promise 组合。

const ratings = [5, 4, 5]
let sum = 0

const sumFunction = async (a, b) => a + b

ratings.forEach(async rating => {
  sum = await sumFunction(sum, rating)
})

console.log(sum)
// 期望的输出:14
// 实际的输出:0
使用 forEach()
输出数组元素每一行记录
const logArrayElements = (element, index /*, array */) => {
  console.log(`a[${index}] = ${element}`)
}
let arr = [2, 5, , 9]
// 注意,索引 2 被跳过,因为数组中这个位置没有内容
arr.forEach(logArrayElements)
// a[0] = 2
// a[1] = 5
// a[3] = 9
使用 thisArg
//按照每个数组中的元素值,更新一个对象的属性
//因为 thisArg 参数(this)传给了 forEach(),每次调用时,它都被传给 callbackFn 函数,作为它的 this 值。
//备注: 如果使用箭头函数表达式来传入函数参数, thisArg 参数会被忽略,因为箭头函数在词法上绑定了 this 值。
class Counter {
  constructor() {
    this.sum = 0
    this.count = 0
  }
  add(array) {
    // 只有函数表达式才有自己的 this 绑定
    array.forEach(function countEntry(entry) {
      this.sum += entry
      ++this.count
    }, this)
  }
}
const obj = new Counter()
obj.add([2, 5, 9])
console.log(obj.count)
// 3
console.log(obj.sum)
// 16
对象复制函数

下面的代码会创建一个给定对象的副本。创建对象的副本有不同的方法,以下是只是一种方法,并解释了 Array.prototype.forEach() 是如何使用 Object.* 实用工具函数。

const copy = obj => {
  const copy = Object.create(Object.getPrototypeOf(obj))
  const propNames = Object.getOwnPropertyNames(obj)
  propNames.forEach(name => {
    const desc = Object.getOwnPropertyDescriptor(obj, name)
    Object.defineProperty(copy, name, desc)
  })
  return copy
}
const obj1 = { a: 1, b: 2 }
const obj2 = copy(obj1)
console.log(obj1, obj2)
// {a: 1, b: 2} {a: 1, b: 2}
在迭代期间修改数组
const words = ['one', 'two', 'three', 'four']
words.forEach(word => {
  console.log(word)
  if (word === 'two') {
    words.shift() //'one' 将从数组中删除
  }
})
// one
// two
// four
console.log(words)
// ['two', 'three', 'four']
在稀疏数组上使用 forEach()
const arraySparse = [1, 3 /* empty */, , 7]
let numCallbackRuns = 0
arraySparse.forEach(element => {
  console.log({ element })
  numCallbackRuns++
})
console.log({ numCallbackRuns })
// { element: 1 }
// { element: 3 }
// { element: 7 }
// { numCallbackRuns: 3 }
在非数组对象上调用 forEach()
// forEach() 方法读取 this 的 length 属性,然后访问每个整数索引。
const arrayLike = {
  length: 3,
  0: 2,
  1: 3,
  2: 4
}
Array.prototype.forEach.call(arrayLike, x => console.log(x))
// 2
// 3
// 4

Array.from()

Array.from() 从可迭代或类数组对象创建一个新的浅拷贝的数组实例。

Array.from() 可以通过以下方式来创建数组对象:

  • 可迭代对象(例如 Map 和 Set 对象);或者,如果对象是不可迭代的,
  • 类数组对象(带有 length 属性和索引元素的对象)。

Array.from() 绝不会创建稀疏数组。如果 arrayLike 对象缺少一些索引属性,那么这些属性在新数组中将是 undefined。

Array.from() 有一个可选的参数 mapFn,该参数允许你在创建数组时为每个元素执行一个函数,类似于 map()。更明确地说,Array.from(obj, mapFn, thisArg) 和 Array.from(obj).map(mapFn, thisArg) 具有相同的结果,只是它不会创建中间数组,并且 mapFn 仅接受两个参数(element、index),不接受数组,因为数组仍然在构建中。

Array.from() 方法是一个通用的工厂方法。例如,如果一个数组类的子类继承 from() 方法,继承的 from() 方法将返回新的子类的实例,而不是数组的实例。事实上,this 值可以是任意的构造函数,只要该构造函数接受一个表示新数组长度的单个参数。当一个迭代器对象作为类数组传递时,不带参数调用构造函数;当传递类数组对象时,将携带类数组对象的规范化长度调用构造函数。迭代完成时,将再次设置最终的 length。如果 this 值并不是构造函数,则使用 Array 构造函数。

使用 from()
从字符串构建数组
console.log(Array.from('foo'))
// [ "f", "o", "o" ]
从 Set 构建数组
const set = new Set(['foo', 'bar', 'baz', 'foo'])
console.log(Array.from(set))
// [ "foo", "bar", "baz" ]
从 Map 构建数组
const map = new Map([
  [1, 2],
  [2, 4],
  [4, 8]
])
console.log(Array.from(map))
// [[1, 2], [2, 4], [4, 8]]

const mapper = new Map([
  ['1', 'a'],
  ['2', 'b']
])
console.log(Array.from(mapper.values()))
// ['a', 'b']
console.log(Array.from(mapper.keys()))
// ['1', '2']
从 NodeList 构建数组
// 根据 DOM 元素的属性创建一个数组
const img = document.createElement('img')
img.src = 'http://1.png'
document.body.appendChild(img)
const images = document.querySelectorAll('img')
const sources = Array.from(images, image => image.src)
console.log(sources)
// ['http://1.png/']
const insecureSources = sources.filter(link => link.startsWith('http://'))
console.log(insecureSources)
// ['http://1.png/']
从类数组对象构建数组(arguments)
function f() {
  return Array.from(arguments)
}
console.log(f(1, 2, 3))
// [ 1, 2, 3 ]
使用箭头函数和 Array.from()
// 使用箭头函数作为映射函数去操作多个元素
let arr = Array.from([1, 2, 3], x => x + x)
console.log(arr)
// [2, 4, 6]

// 生成一个数字序列。因为数组在每个位置都使用 `undefined` 初始化,下面的 `v` 值将是 `undefined`
let arr1 = Array.from({ length: 5 }, (v, i) => i)
console.log(arr1)
// [0, 1, 2, 3, 4]
序列生成器(range)
// 序列生成器函数(通常称为“range”,例如 Clojure、PHP 等)
const range = (start, stop, step) => Array.from({ length: (stop - start) / step + 1 }, (_, i) => start + i * step)

// 生成的数字范围 0..4
let arr2 = range(0, 4, 1)
console.log(arr2)
// [0, 1, 2, 3, 4]

// 生成的数字范围 1..10,步长为 2
let arr3 = range(1, 10, 2)
console.log(arr3)
// [1, 3, 5, 7, 9]

// 使用 Array.from 生成字母表,并将其序列排序
let arr4 = range('A'.charCodeAt(0), 'Z'.charCodeAt(0), 1).map(x => String.fromCharCode(x))
console.log(arr4)
// ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
在非数组构造函数上调用 from()

from() 方法可以在任何构造函数上调用,只要该构造函数接受一个表示新数组长度的单个参数。

function NotArray(len) {
  console.log('NotArray called with length', len)
}
// 可迭代对象
console.log(Array.from.call(NotArray, new Set(['foo', 'bar', 'baz'])))
// NotArray called with length undefined
// NotArray { '0': 'foo', '1': 'bar', '2': 'baz', length: 3 }

// 类数组对象
console.log(Array.from.call(NotArray, { length: 1, 0: 'foo' }))
// NotArray called with length 1
// NotArray { '0': 'foo', length: 1 }

当 this 值不是构造函数,返回一个普通的数组对象。

console.log(Array.from.call({}, { length: 1, 0: 'foo' }))
// [ 'foo' ]

Array.prototype.includes()

includes() 判断一个数组是否包含一个指定的值,包含则返回 true,否则返回 false。

includes() 方法使用零值相等算法将 searchElement 与数组中的元素进行比较。0 值都被认为是相等的,不管符号是什么。(即 -0 等于 0),但 false 不被认为与 0 相同。NaN 可以被正确搜索到。

当在稀疏数组上使用时, includes() 方法迭代空槽,就像它们的值是 undefined 一样。

includes() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。

语法
includes(searchElement)
includes(searchElement, fromIndex)
searchElement

需要查找的值。

fromIndex 可选

开始搜索的索引(从零开始),会转换为整数。

  • 负索引从数组末尾开始计数——如果 fromIndex < 0,那么实际使用的是 fromIndex + array.length。然而在这种情况下,数组仍然从前往后进行搜索。
  • 如果 fromIndex < -array.length 或者省略 fromIndex,则使用 0,这将导致整个数组被搜索。
  • 如果 fromIndex >= array.length,则不会搜索数组并返回 false。
返回值

一个布尔值,如果在数组中(或者在 fromIndex 所指示的数组部分中,如果指定 fromIndex 的话)找到 searchElement 值,则该值为 true。

使用 includes()
let res = [1, 2, 3].includes(2)
console.log(res)
// true
res = [1, 2, 3].includes(4)
console.log(res)
// false
res = [1, 2, 3].includes(3, 3)
console.log(res)
// false
res = [1, 2, 3].includes(3, -1)
console.log(res)
// true
res = [1, 2, NaN].includes(NaN)
console.log(res)
// true
res = ['1', '2', '3'].includes(3)
console.log(res)
// false

// 如果 fromIndex 大于等于数组的长度,则将直接返回 false,且不搜索该数组。
const arr = ['a', 'b', 'c']
let res1 = arr.includes('c', 3)
console.log(res1)
// false
res1 = arr.includes('c', 100)
console.log(res1)
// false

// 如果 fromIndex 为负值,计算出的索引将作为开始搜索 searchElement 的位置。如果计算出的索引小于 0,则整个数组都会被搜索。
// 数组长度为 3
// fromIndex 为 -100
// 计算出的索引为 3 + (-100) = -97
const arr1 = ['a', 'b', 'c']
let res2 = arr1.includes('a', -100)
console.log(res2)
// true
res2 = arr1.includes('b', -100)
console.log(res2)
// true
res2 = arr1.includes('c', -100)
console.log(res2)
// true
res2 = arr1.includes('a', -2)
console.log(res2)
// false
对稀疏数组使用 includes() 方法

在稀疏数组中搜索 undefined,得到 true 。

console.log([1, , 3].includes(undefined))
// true
在非数组对象上调用 includes() 方法
// includes() 方法读取 this 的 length 属性,然后访问每个整数索引。
const arrayLike = {
  length: 3,
  0: 2,
  1: 3,
  2: 4
}
console.log(Array.prototype.includes.call(arrayLike, 2))
// true
console.log(Array.prototype.includes.call(arrayLike, 1))
// false

Array.prototype.indexOf()

indexOf() 返回数组中第一次出现给定元素的下标,如果不存在则返回 -1。

indexOf() 使用严格相等(与 === 运算符使用的算法相同)将 searchElement 与数组中的元素进行比较。NaN 值永远不会被比较为相等,因此当 searchElement 为 NaN 时 indexOf() 总是返回 -1。

indexOf() 方法会跳过稀疏数组中的空槽。

indexOf() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。

语法
indexOf(searchElement)
indexOf(searchElement, fromIndex)
searchElement

数组中要查找的元素。

fromIndex 可选

开始搜索的索引(从零开始),会转换为整数。

  • 负索引从数组末尾开始计数——如果 frommindex < 0,使用 frommindex + array.length。注意,在这种情况下,仍然从前到后搜索数组。
  • 如果 fromIndex < -array.length 或者省略了 fromIndex ,将使用 0,而导致整个数组被搜索。
  • 如果 fromIndex >= array.length,数组不会继续搜索并返回 -1。
返回值

首个被找到的元素在数组中的索引位置; 若没有找到则返回 -1。

使用 indexOf()
const arr = [2, 9, 9, NaN]
let res = arr.indexOf(2)
console.log(res)
// 0
res = arr.indexOf(7)
console.log(res)
// -1
res = arr.indexOf(9, 2)
console.log(res)
// 2
res = arr.indexOf(2, -1)
console.log(res)
// -1
res = arr.indexOf(2, -4)
console.log(res)
// 0
// 没法使用 indexOf() 来搜索 NaN
res = arr.indexOf(NaN)
console.log(res)
// -1
找出指定元素出现的所有位置
const indices = []
const array = ['a', 'b', 'a', 'c', 'a', 'd']
const element = 'a'
let idx = array.indexOf(element)
while (idx !== -1) {
  indices.push(idx)
  idx = array.indexOf(element, idx + 1)
}
console.log(indices)
// [0, 2, 4]
在稀疏数组中使用 indexOf()

不能使用 indexOf() 在稀疏数组中搜索空槽。

console.log([1, , 3].indexOf(undefined))
// -1
在非数组对象上调用 indexOf()

indexOf() 方法读取 this 的 length 属性,然后访问每个整数索引。

const arrayLike = {
  length: 3,
  0: 2,
  1: 3,
  2: 4
}
console.log(Array.prototype.indexOf.call(arrayLike, 2))
// 0
console.log(Array.prototype.indexOf.call(arrayLike, 5))
// -1

Array.isArray()

Array.isArray() 判断传递的值是否是一个 Array,如果是则返回true,否则false。

Array.isArray() 判断 value 是否为 Array,如果 value 是 Array,则为 true;否则为 false。如果 value 是 TypedArray 实例,则总是返回 false。

Array.isArray() 检查传递的值是否为 Array。它不检查值的原型链,也不依赖于它所附加的 Array 构造函数。对于使用数组字面量语法或 Array 构造函数创建的任何值,它都会返回 true。这使得它可以安全地使用跨领域(cross-realm)对象,其中 Array 构造函数的标识是不同的,因此会导致 instanceof Array 失败。

Array.isarray() 也拒绝原型链中带有 Array.prototype,而实际不是数组的对象,但 instanceof Array 会接受。

使用 isArray()
let res = Array.isArray([])
console.log(res)
// true
res = Array.isArray([1])
console.log(res)
// true
res = Array.isArray(new Array())
console.log(res)
// true
res = Array.isArray(new Array('a', 'b', 'c', 'd'))
console.log(res)
// true
res = Array.isArray(new Array(3))
console.log(res)
// true
// 鲜为人知的事实:其实 Array.prototype 也是一个数组:
res = Array.isArray(Array.prototype)
console.log(res)
// true

// 下面的函数调用都返回 false
res = Array.isArray()
console.log(res)
// false
res = Array.isArray({})
console.log(res)
// false
res = Array.isArray(null)
console.log(res)
// false
res = Array.isArray(undefined)
console.log(res)
// false
res = Array.isArray(17)
console.log(res)
// false
res = Array.isArray('Array')
console.log(res)
// false
res = Array.isArray(true)
console.log(res)
// false
res = Array.isArray(false)
console.log(res)
// false
res = Array.isArray(new Uint8Array(32))
console.log(res)
// false
// 这不是一个数组,因为它不是使用数组字面量语法或 Array 构造函数创建的
res = Array.isArray({ __proto__: Array.prototype })
console.log(res)
// false
instanceof vs. Array.isArray()

当检测 Array 实例时,Array.isArray 优于 instanceof,因为 Array.isArray 能跨领域工作。

const iframe = document.createElement('iframe')
document.body.appendChild(iframe)
const xArray = window.frames[window.frames.length - 1].Array
const arr = new xArray(1, 2, 3)
console.log(arr)
// [1, 2, 3]

// 正确检查 Array
let res1 = Array.isArray(arr)
console.log(res1)
// true
// arr 的原型是 xArray.prototype,它是一个不同于 Array.prototype 的对象
res1 = arr instanceof Array
console.log(res1)
// false

Array.prototype.join()

join() 方法将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串,用逗号或指定的分隔符字符串分隔。如果数组只有一个元素,那么将返回该元素而不使用分隔符

所有数组元素被转换成字符串并连接到一个字符串中。如果一个元素是 undefined 或 null,它将被转换为空字符串,而不是字符串 “undefined” 或 “null”。

Array.prototype.toString() 会在内部访问 join 方法,不带参数。覆盖一个数组实例的 join 也将覆盖它的 toString 行为。

当在稀疏数组上使用时,join() 方法迭代空槽,就像它们的值为 undefined 一样。

join() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。

使用 join()
const a = ['Wind', 'Water', 'Fire']
let str = a.join()
console.log(str)
// 'Wind,Water,Fire'
str = a.join(', ')
console.log(str)
// 'Wind, Water, Fire'
str = a.join(' + ')
console.log(str)
// 'Wind + Water + Fire'
str = a.join('')
console.log(str)
// 'WindWaterFire'
在稀疏数组上使用 join()
// join() 将空槽视为 undefined,并产生额外的分隔符:
console.log([1, , 3].join())
// '1,,3'
console.log([1, undefined, 3].join())
// '1,,3'
在非数组对象上调用 join()

join() 方法读取 this 的 length 属性,然后访问每个整数索引。

const arrayLike = {
  length: 3,
  0: 2,
  1: 3,
  2: 4
}
console.log(Array.prototype.join.call(arrayLike))
// 2,3,4
console.log(Array.prototype.join.call(arrayLike, '.'))
// 2.3.4

Array.prototype.keys()

keys() 返回一个包含数组中每个索引的键的新数组迭代器 (en-US)对象。

当用于稀疏数组时,keys() 方法迭代空槽,就像它们的值为 undefined 一样。

keys() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。

entries(),keys()和values() —— 用于遍历数组。它们都返回一个遍历器对象,可以用for…of循环进行遍历。

区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。

使用 keys()
const array1 = ['a', 'b', 'c']
const iterator = array1.keys()
console.log(iterator)
// Array Iterator {}
for (const key of iterator) {
  console.log(key)
}
// 0
// 1
// 2
在稀疏数组中使用 keys()

与 Object.keys() 只包含数组中实际存在的键不同,keys() 迭代器不会忽略缺失属性的键。

const arr = ['a', , 'c']
const sparseKeys = Object.keys(arr)
const denseKeys = [...arr.keys()]
console.log(sparseKeys)
// ['0', '2']
console.log(denseKeys)
// [0, 1, 2]
在非数组对象上调用 keys()

keys() 方法读取 this 的 length 属性,然后生成 0 到 length - 1 之间的所有整数索引。实际并不会访问索引。

const arrayLike = {
  length: 3
}
for (const entry of Array.prototype.keys.call(arrayLike)) {
  console.log(entry)
}
// 0
// 1
// 2

Array.prototype.lastIndexOf()

lastIndexOf() 方法返回数组中给定元素最后一次出现的索引,如果不存在则返回 -1。该方法从 fromIndex 开始向前搜索数组。

lastIndexOf 使用严格相等(与 === 运算符使用的算法相同)比较 searchElement 和数组中的元素。NaN 值永远不会被比较为相等,因此当 searchElement 为 NaN 时 lastIndexOf() 总是返回 -1。

lastIndexOf() 方法会跳过稀疏数组中的空槽。

lastIndexOf() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。

语法
lastIndexOf(searchElement)
lastIndexOf(searchElement, fromIndex)
searchElement

被查找的元素。

fromIndex 可选

以 0 起始的索引,表明反向搜索的起始位置,会被转换为整数。

  • 如果 fromIndex < 0,则从数组末尾开始倒数计数——即使用 fromIndex + array.length 的值。
  • 如果 fromIndex < -array.length,则不搜索数组并返回 -1。从概念上讲,你可以把它想象成从数组开始之前不存在的位置开始反向搜索,这条路径上没有任何数组元素,因此 searchElement 永远不会被找到。
  • 如果 fromIndex >= array.length 或者省略了 fromIndex,则使用 array.length - 1,这会导致搜索整个数组。可以将其理解为从数组尾部之后不存在的位置开始向前搜索。最终会访问到数组最后一个元素,并继续向前开始实际搜索数组元素。
返回值
  • 数组中该元素最后一次出现的索引,如未找到返回 -1。
使用 lastIndexOf()
const numbers = [2, 5, 9, 2, NaN]
let res = numbers.lastIndexOf(2)
console.log(res)
// 3
res = numbers.lastIndexOf(7)
console.log(res)
// -1
res = numbers.lastIndexOf(2, 3)
console.log(res)
// 3
res = numbers.lastIndexOf(2, 2)
console.log(res)
// 0
res = numbers.lastIndexOf(2, -3)
console.log(res)
// 0
res = numbers.lastIndexOf(2, -1)
console.log(res)
// 3
// 不能用 lastIndexOf() 来搜索 NaN
res = numbers.lastIndexOf(NaN)
console.log(res)
// -1
查找元素出现的所有索引

下例使用 lastIndexOf 查找到一个元素在数组中所有的索引(下标),并在找到它们时用 push 将它们添加到另一个数组中。

需要注意的是,这里必须单独处理 idx === 0 的情况,因为如果该元素是数组的第一个元素,则无论 fromIndex 参数的值为何,它总是会被找到。这与 indexOf 方法不同。

const indices = []
const array = ['a', 'b', 'a', 'c', 'a', 'd']
const element = 'a'
let idx = array.lastIndexOf(element)
while (idx !== -1) {
  indices.push(idx)
  idx = idx > 0 ? array.lastIndexOf(element, idx - 1) : -1
}

console.log(indices)
// [4, 2, 0]
在稀疏数组上使用 lastIndexOf()

不能使用 lastIndexOf() 来搜索稀疏数组中的空槽。

console.log([1, , 3].lastIndexOf(undefined))
// -1
在非数组对象上调用 lastIndexOf()

lastIndexOf() 方法读取 this 的 length 属性,然后访问每个整数索引。

const arrayLike = {
  length: 3,
  0: 2,
  1: 3,
  2: 2
}
console.log(Array.prototype.lastIndexOf.call(arrayLike, 2))
// 2
console.log(Array.prototype.lastIndexOf.call(arrayLike, 5))
// -1

Array.prototype.map()

map()方法返回一个由原数组中的每个元素调用一个指定方法后的返回值组成的新数组,它不会改变原来的数组。

map() 方法是一个迭代方法。它为数组中的每个元素调用一次提供的 callbackFn 函数,并用结果构建一个新数组。

callbackFn 仅在已分配值的数组索引处被调用。它不会在稀疏数组中的空槽处被调用。

map() 方法是一个复制方法。它不会改变 this。然而,作为 callbackFn 提供的函数可以更改数组。请注意,在第一次调用 callbackFn 之前,数组的长度已经被保存。因此:

  • 当开始调用 map() 时,callbackFn 将不会访问超出数组初始长度的任何元素。
  • 对已访问索引的更改不会导致再次在这些元素上调用 callbackFn。
  • 如果数组中一个现有的、尚未访问的元素被 callbackFn 更改,则它传递给 callbackFn 的值将是该元素被修改后的值。被删除的元素则不会被访问。

map() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。

由于 map 创建一个新数组,在没有使用返回的数组的情况下调用它是不恰当的;应该使用 forEach 或 for…of 作为代替。

使用 map()
求数组中每个元素的平方根
const numbers = [1, 4, 9]
const roots = numbers.map(num => Math.sqrt(num))
console.log('numbers:', numbers)
// numbers: [1, 4, 9]
console.log('roots:', roots)
// numbers: [1, 2, 3]
使用 map 重新格式化数组中的对象
const kvArray = [
  { key: 1, value: 10 },
  { key: 2, value: 20 },
  { key: 3, value: 30 }
]
const reformattedArray = kvArray.map(({ key, value }) => ({ [key]: value }))
console.log(reformattedArray)
// [{ 1: 10 }, { 2: 20 }, { 3: 30 }]
console.log(kvArray)
// [
//   { key: 1, value: 10 },
//   { key: 2, value: 20 },
//   { key: 3, value: 30 }
// ]
使用包含单个参数的函数来映射一个数字数组
const nums = [1, 4, 9]
const res = numbers.map(num => num * 2)
console.log(res) // [2, 8, 18]
console.log(nums) // [1, 4, 9]
将 parseInt() 与 map() 一起使用
const returnInt = element => parseInt(element, 10)
let res1 = ['1', '2', '3'].map(returnInt)
console.log(res1)
// [1, 2, 3]
// 实际结果是一个数字数组(如预期)

// 与上面相同,但使用简洁的箭头函数语法
let res2 = ['1', '2', '3'].map(str => parseInt(str))
console.log(res2)
// [1, 2, 3]

// 实现上述目标更简单的方法,同时避免了“骗招”:
let res3 = ['1', '2', '3'].map(Number)
console.log(res3)
// [1, 2, 3]

// 但与 parseInt() 不同,Number() 还会返回一个浮点数或(解析)指数表示法:
let res4 = ['1.1', '2.2e2', '3e300'].map(Number)
console.log(res4)
// [1.1, 220, 3e+300]

// 为了进行比较,如果我们对上面的数组使用 parseInt():
let res5 = ['1.1', '2.2e2', '3e300'].map(str => parseInt(str))
console.log(res5)
// [1, 2, 3]

const arr6 = ['10', '10', '10']
const res6 = arr6.map(parseInt)
console.log(res6)
// [10, NaN, 2]
映射包含 undefined 的数组

当返回 undefined 或没有返回任何内容时:

const arr7 = [1, 2, 3, 4]
const res7 = arr7.map((num, index) => {
  if (index < 3) {
    return num
  }
})
console.log(res7)
// [1, 2, 3, undefined]
在稀疏数组上使用 map()

稀疏数组在使用 map() 方法后仍然是稀疏的。空槽的索引在返回的数组中仍然为空,并且回调函数不会对它们进行调用。

console.log(
  [1, , 3].map((x, index) => {
    console.log(`Visit ${index}`)
    return x * 2
  })
)
// Visit 0
// Visit 2
// [2, empty, 6]
在非数组对象上调用 map()

map() 方法读取 this 的 length 属性,然后访问每个整数索引。

const arrayLike = {
  length: 3,
  0: 2,
  1: 3,
  2: 4
}
console.log(Array.prototype.map.call(arrayLike, x => x ** 2))
// [ 4, 9, 16 ]

Array.prototype.pop()

pop() 方法从数组中删除最后一个元素,并返回该元素的值。此方法会更改数组的长度。

pop() 方法从一个数组中删除并返回最后一个元素给调用者。如果你在空数组上调用 pop(),它会返回 undefined。

Array.prototype.shift() 和 pop() 有类似的行为,但是它是作用在数组的第一个元素上的。

pop() 是修改方法。其改变了 this 的长度和内容。如果你想要 this 不变,但是返回一个新的最后一个元素被移除的数组,你可以使用 arr.slice(0, -1) 来代替。

pop() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。虽然字符串也是类数组对象,但是由于其不能被修改,所以 pop() 方法并不能应用在字符串上。

使用 pop()
// 删除掉数组的最后一个元素
const myFish = ['angel', 'clown', 'mandarin', 'sturgeon']
const popped = myFish.pop()
console.log(myFish)
// ['angel', 'clown', 'mandarin' ]
console.log(popped)
// 'sturgeon'
在非数组对象上调用 pop()

pop() 方法会读取 this 上的 length 属性。如果规范化的 length 属性为 0,length 会被再次设置为 0(鉴于之前可能是负数或者 undefined)。否则,返回并删除位于 length - 1 处的属性。

const arrayLike = {
  length: 3,
  unrelated: 'foo',
  2: 4
}
console.log(Array.prototype.pop.call(arrayLike))
// 4
console.log(arrayLike)
// { length: 2, unrelated: 'foo' }

const plainObj = {}
// 没有 length 属性,所以长度为 0
Array.prototype.pop.call(plainObj)
console.log(plainObj)
// { length: 0 }
以类数组的方式使用对象

push 和 pop 方法是通用的,我们可以利用这一点来编写更灵活的代码——如以下示例所示。

请注意,在此示例中,我们不会创建数组来存储对象集合。相反,我们将集合存储在对象本身上,并在 Array.prototype.push 和 Array.prototype.pop 上使用 call 来欺骗这些方法,让它们认为我们正在处理一个数组。

const collection = {
  length: 0,
  addElements(...elements) {
    // 每次添加元素时
    // obj.length 都会自动增加

    // 返回 push 方法的返回值,即 length 属性的新值
    return [].push.call(this, ...elements)
  },
  removeElement() {
    // 每次移除元素时
    // obj.length 都会自动减少

    // 返回 pop 方法的返回值,即被移除的元素
    return [].pop.call(this)
  }
}

collection.addElements(10, 20, 30)
console.log(collection.length)
// 3
console.log(collection)
// {0: 10, 1: 20, 2: 30, length: 3, addElements: ƒ, removeElement: ƒ}
collection.removeElement()
console.log(collection.length)
// 2
console.log(collection)
// {0: 10, 1: 20, length: 2, addElements: ƒ, removeElement: ƒ}

Array.prototype.push()

push() 将指定的元素添加到数组的末尾,并返回新的数组长度。改变原数组。

push() 方法将值追加到一个数组中。

Array.prototype.unshift() 有着和 push() 相似的行为,但是其作用于数组的开头。

push() 方法是一个修改方法。它改变了 this 的内容和长度。如果你希望 this 的值保持不变,但返回一个末尾追加了元素的新数组,你可以使用 arr.concat([element0, element1, /* … ,*/ elementN]) 来代替。请注意,这些元素需要被包装在一个额外的数组中——否则,如果元素本身是一个数组,由于 concat() 的行为,它将被展开而不是作为单个元素添加到原数组的末尾。

push() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。虽然字符串也是类似数组的,但这个方法不适用于它,因为字符串是不可变的。

使用 push()
// 在数组中添加元素
const sports = ['soccer', 'baseball']
const total = sports.push('football', 'swimming')
console.log(sports)
// ['soccer', 'baseball', 'football', 'swimming']
console.log(total)
// 4

// 使用展开语法来将第二个数组的所有元素添加到第一个数组中。
const vegetables = ['parsnip', 'potato']
const moreVegs = ['celery', 'beetroot']
// 合并第二个数组到第一个数组中
vegetables.push(...moreVegs)
console.log(vegetables)
// ['parsnip', 'potato', 'celery', 'beetroot']
在非数组对象中使用 push()

push() 方法会读取 this 的 length 属性。然后,它将 this 的每个索引从 length 开始设置,并将参数传递给 push()。最后,它将 length 设置成之前的长度加上已添加元素的数量。

const arrayLike = {
  length: 3,
  unrelated: 'foo',
  2: 4
}
Array.prototype.push.call(arrayLike, 1, 2)
console.log(arrayLike)
// { '2': 4, '3': 1, '4': 2, length: 5, unrelated: 'foo' }

const plainObj = {}
// 这里没有长度属性,所以长度为 0
Array.prototype.push.call(plainObj, 1, 2)
console.log(plainObj)
// { '0': 1, '1': 2, length: 2 }
以类似数组的方式使用对象

如上所述,push 被有意设计为通用的,我们可以利用这一点。就像这个例子所展示的,Array.prototype.push 可以很好的处理对象。

注意,我们没有创建一个数组来存储对象集合。相反,我们将集合存储在对象本身上,并通过 Array.prototype.push 的 call 来调用该方法,让它认为我们正在处理一个数组——归功于 JavaScript 允许我们以任何我们想要的方式建立执行上下文的方式,这样是可行的。

const obj = {
  length: 0,
  addElem(elem) {
    // obj.length 在每次添加元素时自动增加
    return [].push.call(this, elem)
  }
}
// 让我们添加一些空对象来说明
obj.addElem({})
obj.addElem({})
console.log(obj)
// {0: {}, 1: {}, length: 2, addElem: ƒ}

Array.prototype.reduce()

reduce() 接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。

reduce() 方法是一个迭代方法。它按升序对数组中的所有元素运行一个“reducer”回调函数,并将它们累积到一个单一的值中。每次调用时,callbackFn 的返回值都作为 accumulator 参数传递到下一次调用中。accumulator 的最终值(也就是在数组的最后一次迭代中从 callbackFn 返回的值)将作为 reduce() 的返回值。

callbackFn 仅对已分配值的数组索引进行调用。不会对稀疏数组中的空槽进行调用。

与其他迭代方法不同,reduce() 不接受 thisArg 参数。callbackFn 调用时始终以 undefined 作为 this 的值,如果 callbackFn 未处于严格模式,则该值将被替换为 globalThis。

reduce() 是函数式编程中的一个核心概念,在函数式编程中,不可能改变任何值,因此为了累积数组中的所有值,必须在每次迭代中返回一个新的累加器。这种约定也适用于 JavaScript 的 reduce():应该在可能的情况下使用展开语法或其他复制方法来创建新的数组和对象作为累加器,而不是改变现有的累加器。如果你决定改变累加器而不是复制它,请记得仍然在回调中返回修改后的对象,否则下一次迭代将收到 undefined。

reduce() 不会改变被调用的数组,但是作为 callbackFn 提供的函数可能会改变数组。但需要注意的是,在第一次调用 callbackFn 之前,数组的长度会被保存。因此:

  • 当开始调用 reduce() 时,callbackFn 将不会访问超出数组初始长度的任何元素。
  • 对已访问索引的更改不会导致再次在这些元素上调用 callbackFn。
  • 如果数组中一个现有的、尚未访问的元素被 callbackFn 更改,则它传递给 callbackFn 的值将是该元素被修改后的值。被删除的元素则不会被访问。

reduce() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。

使用 reduce()
边界情况

如果数组只有一个元素(无论位置如何)且未提供 initialValue,或者提供了 initialValue 但数组为空,则将返回该单个值,而不调用 callbackFn。

如果提供了 initialValue 且数组不为空,则 reduce 方法将始终从索引 0 开始调用回调函数。

如果未提供 initialValue,则对于长度大于 1、等于 1 和 0 的数组,reduce 方法将有不同的表现,如以下示例所示:

const getMax = (a, b) => Math.max(a, b)

// 从索引 0 开始为数组中的每个元素调用回调函数
let res = [1, 100].reduce(getMax, 50)
console.log(res)
// 100
res = [50].reduce(getMax, 10)
console.log(res)
// 50

// 仅为索引 1 处的元素调用回调函数
res = [1, 100].reduce(getMax)
console.log(res)
// 100

// 不调用回调函数
res = [50].reduce(getMax)
console.log(res)
// 50
res = [].reduce(getMax, 1)
console.log(res)
// 1

// res = [].reduce(getMax)
console.log(res)
// Uncaught TypeError: Reduce of empty array with no initial value
无初始值时 reduce() 如何运行
const array = [15, 16, 17, 18, 19]

function reducer(accumulator, currentValue, index) {
  const returns = accumulator + currentValue
  console.log(`accumulator: ${accumulator}, currentValue: ${currentValue}, index: ${index}, returns: ${returns}`)
  return returns
}

let res1 = array.reduce(reducer)
console.log(res1)
// accumulator: 15, currentValue: 16, index: 1, returns: 31
// accumulator: 31, currentValue: 17, index: 2, returns: 48
// accumulator: 48, currentValue: 18, index: 3, returns: 66
// accumulator: 66, currentValue: 19, index: 4, returns: 85
// 85
// array 参数在整个过程中始终不会改变——它始终是 [15, 16, 17, 18, 19]。reduce() 返回的值将是最后一次回调返回值(85)。
有初始值时 reduce() 如何运行
let res2 = [15, 16, 17, 18, 19].reduce((accumulator, currentValue) => accumulator + currentValue, 10)
console.log(res2)
// 95
求对象数组中值的总和

为了对包含在对象数组中的值进行求和,必须提供一个 initialValue,以便每个项都通过回调函数处理。

const objects = [{ x: 1 }, { x: 2 }, { x: 3 }]
const sum = objects.reduce((accumulator, currentValue) => accumulator + currentValue.x, 0)
console.log(sum)
// 6
使用 reduce() 来替代 .filter().map()

使用 filter() 和 map() 会遍历数组两次,但是你可以使用 reduce() 只遍历一次并实现相同的效果,从而更高效。(如果你喜欢使用 for 循环,你可以在遍历一次时使用 forEach() 进行过滤和映射。)

const numbers = [-5, 6, 2, 0]

const doubledPositiveNumbers = numbers.reduce((accumulator, currentValue) => {
  if (currentValue > 0) {
    const doubled = currentValue * 2
    return [...accumulator, doubled]
  }
  return accumulator
}, [])

console.log(doubledPositiveNumbers)
// [12, 4]
按顺序运行 Promise
/**
 * 链接一系列 Promise 处理程序。
 *
 * @param {array} arr ——一个 Promise 处理程序列表,每个处理程序接收前一个处理程序解决的结果并返回另一个 Promise。
 * @param {*} input ——开始调用 Promise 链的初始值
 * @return {Object} ——由一系列 Promise 链接而成的 Promise
 */
function runPromiseInSequence(arr, input) {
  return arr.reduce((promiseChain, currentFunction) => promiseChain.then(currentFunction), Promise.resolve(input))
}

// Promise 函数 1
function p1(a) {
  return new Promise((resolve, reject) => {
    resolve(a * 5)
  })
}

// Promise 函数 2
function p2(a) {
  return new Promise((resolve, reject) => {
    resolve(a * 2)
  })
}

// 函数 3——将由 `.then()` 包装在已解决的 Promise 中
function f3(a) {
  return a * 3
}

// Promise 函数 4
function p4(a) {
  return new Promise((resolve, reject) => {
    resolve(a * 4)
  })
}

const promiseArr = [p1, p2, f3, p4]
runPromiseInSequence(promiseArr, 10).then(console.log)
// 1200
在稀疏数组中使用 reduce()

reduce() 会跳过稀疏数组中缺失的元素,但不会跳过 undefined 值。

console.log([1, 2, , 4].reduce((a, b) => a + b))
// 7
console.log([1, 2, undefined, 4].reduce((a, b) => a + b))
// NaN
在非数组对象上调用 reduce()

reduce() 方法读取 this 的 length 属性,然后访问每个整数索引。

const arrayLike = {
  length: 3,
  0: 2,
  1: 3,
  2: 4
}
console.log(Array.prototype.reduce.call(arrayLike, (x, y) => x + y))
// 9

Array.prototype.reduceRight()

reduceRight() 接收一个函数作为累加器,数组中的每个值(从右到左)开始缩减,最终计算为一个值。

reduceRight() 方法是一个迭代方法。它为数组中的所有元素降序调用“reducer”回调函数,并将它们累积到一个单一的值中。

callbackFn 仅为已分配值的数组索引调用。它不会为稀疏数组中的空槽调用。

与其他迭代方法不同,reduceRight() 不接受 thisArg 参数。callbackFn 调用时始终以 undefined 作为 this 的值,如果 callbackFn 未处于严格模式,则该值将被替换为 globalThis。

reduceRighte() 不会改变被调用的数组,但是作为 callbackFn 提供的函数可能会改变数组。但需要注意的是,在第一次调用 callbackFn 之前,数组的长度会被保存。因此:

  • 当开始调用 reduceRight() 时,callbackFn 将不会访问超出数组初始长度的任何元素。
  • 对已访问索引的更改不会导致再次在这些元素上调用 callbackFn。
  • 如果数组中一个现有的、尚未访问的元素被 callbackFn 更改,则它传递给 callbackFn 的值将是该元素被修改后的值。被删除的元素则不会被访问。

reduceRight() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。

使用 reduceRight()
无初始值时 reduceRight() 如何运行

在第一次调用函数时,accumulator 和 currentValue 的可能取值情况有两种。如果在调用 reduceRight 时提供了 initialValue,那么 accumulator 将等于 initialValue,而 currentValue 将等于数组中的最后一个值。如果没有提供 initialValue,则 accumulator 将等于数组中的最后一个值,而 currentValue 将等于倒数第二个值。

如果数组为空且没有提供 initialValue,则会抛出 TypeError 异常。如果数组只有一个元素(无论其位置如何)且没有提供 initialValue,或者提供了 initialValue 但数组为空,则直接返回该单个值,且 callbackFn 不会被调用。

let res = [0, 1, 2, 3, 4].reduceRight((accumulator, currentValue, index, array) => {
  const returns = accumulator + currentValue
  console.log(`accumulator: ${accumulator}, currentValue: ${currentValue}, index: ${index}, returns: ${returns}`)
  return returns
})
console.log(res)
// accumulator: 10, currentValue: 4, index: 4, returns: 14
// accumulator: 14, currentValue: 3, index: 3, returns: 17
// accumulator: 17, currentValue: 2, index: 2, returns: 19
// accumulator: 19, currentValue: 1, index: 1, returns: 20
// accumulator: 20, currentValue: 0, index: 0, returns: 20
// 20
有初始值时 reduceRight() 如何运行
let res1 = [0, 1, 2, 3, 4].reduceRight((accumulator, currentValue, index, array) => {
  const returns = accumulator + currentValue
  console.log(`accumulator: ${accumulator}, currentValue: ${currentValue}, index: ${index}, returns: ${returns}`)
  return returns
}, 10)
console.log(res1)
// accumulator: 4, currentValue: 3, index: 3, returns: 7
// accumulator: 7, currentValue: 2, index: 2, returns: 9
// accumulator: 9, currentValue: 1, index: 1, returns: 10
// accumulator: 10, currentValue: 0, index: 0, returns: 10
// 10
reduce 与 reduceRight 之间的区别
const a = ['1', '2', '3', '4', '5']
const left = a.reduce((prev, cur) => prev + cur)
const right = a.reduceRight((prev, cur) => prev + cur)
console.log(left)
// "12345"
console.log(right)
// "54321"
定义可组合函数

函数组合是一种将函数组合在一起的机制,其中每个函数的输出都作为下一个函数的输入,最后一个函数的输出是最终的结果。在这个例子中,我们使用 reduceRight() 来实现函数组合。

const compose =
  (...args) =>
  value =>
    args.reduceRight((acc, fn) => fn(acc), value)

// Increment passed number
const inc = n => n + 1

// Doubles the passed value
const double = n => n * 2

// using composition function
console.log(compose(double, inc)(2))
// 6
// using composition function
console.log(compose(inc, double)(2))
// 5
在稀疏数组中使用 reduceRight()

reduceRight() 会跳过稀疏数组中缺失的元素,但不会跳过 undefined 值。

console.log([1, 2, , 4].reduceRight((a, b) => a + b))
// 7
console.log([1, 2, undefined, 4].reduceRight((a, b) => a + b))
// NaN
在非数组对象上调用 reduceRight()

reduceRight() 方法读取 this 的 length 属性,然后访问每个整数索引。

const arrayLike = {
  length: 3,
  0: 2,
  1: 3,
  2: 4
}
console.log(Array.prototype.reduceRight.call(arrayLike, (x, y) => x - y))
// -1

Array.prototype.reverse()

reverse() 方法就地反转数组中的元素,并返回同一数组的引用。数组的第一个元素会变成最后一个,数组的最后一个元素变成第一个。换句话说,数组中的元素顺序将被翻转,变为与之前相反的方向。

要在不改变原始数组的情况下反转数组中的元素,使用 toReversed()。

reverse 方法反转数组中元素的位置,改变了数组,并返回该数组的引用。

reverse() 方法会保留空槽。如果源数组是稀疏的,则空槽对应的新索引将被删除,并且也成为空槽。

reverse() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。虽然字符串也是类似于数组的,但这个方法不适用于它们,因为字符串是不可变的。

使用 reverse()
// 创建一个数组 items,其中包含三个元素,然后反转数组。调用 reverse() 会返回反转后的数组 items 的引用。
const items = [1, 2, 3]
let res = items.reverse()
console.log(items)
// [3, 2, 1]
console.log(res)
// [3, 2, 1]
reverse() 方法返回对同一数组的引用

reverse() 方法返回对原始数组的引用,因此改变返回的数组也会改变原始数组。

const numbers = [3, 2, 4, 1, 5]
const reversed = numbers.reverse()
// numbers 和 reversed 的顺序都是颠倒的 [5, 1, 4, 2, 3]
reversed[0] = 5
console.log(numbers[0])
// 5

如果你希望 reverse() 不改变原始数组,但返回一个浅拷贝数组,就像其他数组方法(例如 map())一样,使用 toReversed() 方法。或者,你可以在调用 reverse() 之前做一个浅拷贝,使用展开语法或 Array.from()。

const numbers1 = [3, 2, 4, 1, 5]
// [...numbers] 创建一个浅拷贝,因此 reverse() 不会改变原始数据
const reverted1 = [...numbers1].reverse()
reverted1[0] = 5
console.log(numbers1[0])
// 3
对稀疏数组使用 reverse()

调用 reverse() 后,稀疏数组保持稀疏状态。空槽将以空槽的形式被复制到它们各自的新索引中

console.log([1, , 3].reverse())
// [3, empty, 1]
console.log([1, , 3, 4].reverse())
// [4, 3, empty, 1]
对非数组对象调用 reverse()

reverse() 方法读取 this 的 length 属性。然后,它访问 0 和 length / 2 之间的每个索引,并交换两端对应的两个索引,并在必要时,删除某些属性。

const arrayLike = {
  length: 3,
  unrelated: 'foo',
  2: 4
}
console.log(Array.prototype.reverse.call(arrayLike))
// { '0': 4, length: 3, unrelated: 'foo' }
// 索引“2”被删除了,因为原本的数据中索引“0”不存在了

Array.prototype.shift()

shift() 方法从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度。

shift 方法移除索引为 0 的元素,并将后续元素的下标依次向前移动,然后返回被移除的元素。如果 length 属性的值为 0,则返回 undefined。

pop() 方法有着和 shift() 相似的行为。但是是作用于数组的最后一个元素上的。

shift() 方法是一个改变方法。它改变了 this 的内容和长度。如果你希望保持 this 的值不变,但返回一个删除了第一个元素的新数组,你可以使用 arr.slice(1)。

shift() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。虽然字符串也是类似数组的,但这个方法不适合应用于它们,因为字符串是不可变的。

使用 shift()
移除数组中的一个元素
const myFish = ['angel', 'clown', 'mandarin', 'surgeon']
const shifted = myFish.shift()
console.log('调用 shift 之后:', myFish)
// 调用 shift 之后: ['clown', 'mandarin', 'surgeon']
console.log('被删除的元素:' + shifted)
// 被删除的元素:angel
在 while 循环中使用 shift()

shift() 方法经常用于 while 循环的条件中。下例中每次迭代都会从一个数组中移除下一项元素,直至它成为空数组。

const names = ['Andrew', 'Tyrone', 'Paul', 'Maria', 'Gayatri']

while (typeof (i = names.shift()) !== 'undefined') {
  console.log(i)
}
// Andrew,
// Tyrone
// Paul
// Maria
// Gayatri
在非数组对象上调用 shift()

shift 方法会读取 this 的 length 属性。如果规范化长度为 0,length 再次设置为 0(而之前可能为负值或 undefined)。否则,返回 0 处的属性,其余属性向左移动 1。length 属性递减 1。

const arrayLike = {
  length: 3,
  unrelated: 'foo',
  2: 4
}
console.log(Array.prototype.shift.call(arrayLike))
// undefined
console.log(arrayLike)
// { '1': 4, length: 2, unrelated: 'foo' }

const plainObj = {}
// 这里没有长度属性,所以长度为 0
Array.prototype.shift.call(plainObj)
console.log(plainObj)
// { length: 0 }

Array.prototype.slice()

slice() 返回一个从原数组索引 start 到 end 元素的浅拷贝(包括 start,不包括 end)得到的新的数组。原始数组不会被改变。

slice() 方法是一个复制方法。它不会改变 this,而是返回一个浅拷贝,其中包含了原始数组的一部分相同的元素。

slice() 方法会保留空槽。如果被切片的部分是稀疏的,则返回的数组也是稀疏的。

slice() 方法是通用的。它只要求 this 上有 length 属性和整数键属性

使用 slice()
var arr = [1, 2, 3, 'a', 'b', 'c', 'd']
// 只有起始位置时,截取到数组最后
console.log(arr.slice(2))
// [3, "a", "b", "c", "d"]
// 从起始位置开始到结束位置结束(包含起始不包含结束)
console.log(arr.slice(3, 6))
//["a", "b", "c"]
console.log(arr)
//[1, 2, 3, "a", "b", "c", "d"]
// 接收负数
console.log(arr.slice(-2))
// ["c", "d"]
console.log(arr.slice(-5, 6))
//[3, "a", "b", "c"]
//结束位置小于起始位置,返回空数组。
console.log(arr.slice(-5, -6))
//[]
// 起始位置等于结束位置,返回空数组。
console.log(arr.slice(5, 5))
//[]
console.log(arr.slice(5))
// ["c", "d"]
在稀疏数组上使用 slice()

如果源数组是稀疏数组,slice() 方法返回的数组也会是稀疏数组。

console.log([1, 2, , 4, 5].slice(1, 4))
// [2, empty, 4]
在类数组对象上调用 slice()

slice() 方法会读取 this 对象的 length 属性,然后从 start 到 end 读取整数键属性,并将它们定义在一个新创建的数组中。

const arrayLike = {
  length: 3,
  0: 2,
  1: 3,
  2: 4
}
console.log(Array.prototype.slice.call(arrayLike, 1, 3))
// [ 3, 4 ]
使用 slice() 把类数组对象转化为数组

slice() 方法经常与 bind() 和 call() 一起使用,用于创建一个实用方法,将类数组对象转换为数组。

// 调用 slice() 方法时,会将 this 对象作为第一个参数传入
const slice = Function.prototype.call.bind(Array.prototype.slice)
function list() {
  return slice(arguments)
}
const list1 = list(1, 2, 3)
console.log(list1)
// [1, 2, 3]

Array.prototype.some()

some() 判断数组中是否存在满足条件的项,

some() 方法是一个迭代方法。它为数组中的每个元素调用一次指定的 callbackFn 函数,直到 callbackFn 返回一个真值。如果找到这样的元素,some() 方法将会立即返回 true 并停止遍历数组。否则,如果 callbackFn 对所有元素都返回假值,some() 就会返回 false。

some() 类似于数学中的“存在量词(∃)”。特别地,在对于一个空数组,任何条件下它都返回 false。

callbackFn 仅针对已分配值的数组索引调用。它不会为稀疏数组中的空槽调用。

some() 不会改变调用它的数组,但指定的 callbackFn 函数可以。但是请注意,数组的长度是在第一次调用 callbackFn 之前保存的。因此:

  • 当开始调用 some() 时,callbackFn 将不会访问超出数组初始长度的任何元素。
  • 对已访问索引的更改不会导致再次在这些元素上调用 callbackFn。
  • 如果数组中一个现有的、尚未访问的元素被 callbackFn 更改,则它传递给 callbackFn 的值将是该元素被修改后的值。被删除的元素则不会被访问。

some() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。

使用 some()
// 检测在数组中是否有元素大于 10function isBiggerThan10(element, index, array) {
  return element > 10
}
let res = [2, 5, 8, 1, 4].some(isBiggerThan10)
console.log(res)
// false
res = [12, 5, 8, 1, 4].some(isBiggerThan10)
console.log(res)
// true

// 使用箭头函数测试数组元素的值
res = [2, 5, 8, 1, 4].some(x => x > 10)
console.log(res)
// false
res = [12, 5, 8, 1, 4].some(x => x > 10)
console.log(res)
// true
判断数组元素中是否存在某个值
// 此例为模仿 includes() 方法,若元素在数组中存在,则回调函数返回值为 true :
const fruits = ['apple', 'banana', 'mango', 'guava']

function checkAvailability(arr, val) {
  return arr.some(arrVal => val === arrVal)
}

let res1 = checkAvailability(fruits, 'kela')
console.log(res1)
// false
res1 = checkAvailability(fruits, 'banana')
console.log(res1)
// true
将任意值转换为布尔类型
const TRUTHY_VALUES = [true, 'true', 1]

function getBoolean(value) {
  if (typeof value === 'string') {
    value = value.toLowerCase().trim()
  }

  return TRUTHY_VALUES.some(t => t === value)
}

console.log(getBoolean(false))
// false
console.log(getBoolean('false'))
// false
console.log(getBoolean(1))
// true
console.log(getBoolean('true'))
// true
console.log(getBoolean(0))
// false
在稀疏数组上使用 some()

some() 不会在空槽上运行它的断言函数。

console.log([1, , 3].some(x => x === undefined))
// false
console.log([1, , 1].some(x => x !== 1))
// false
console.log([1, undefined, 1].some(x => x !== 1))
// true
在非数组对象上调用 some()

some() 方法读取 this 的 length 属性,然后访问每个整数索引,直到到达末尾或 callbackFn 返回 true。

const arrayLike = {
  length: 3,
  0: 'a',
  1: 'b',
  2: 'c'
}
console.log(Array.prototype.some.call(arrayLike, x => typeof x === 'number'))
// false

Array.prototype.sort()

sort() 方法就地对数组的元素进行排序,并返回对相同数组的引用。默认排序是将元素转换为字符串,然后按照它们的 UTF-16 码元值升序排序。

由于它取决于具体实现,因此无法保证排序的时间和空间复杂度。

如果想要不改变原数组的排序方法,可以使用 toSorted()

如果没有提供 compareFn,所有非 undefined 的数组元素都会被转换为字符串,并按照 UTF-16 码元顺序比较字符串进行排序。例如“banana”会被排列到“cherry”之前。在数值排序中,9 出现在 80 之前,但因为数字会被转换为字符串,在 Unicode 顺序中“80”排在“9”之前。所有的 undefined 元素都会被排序到数组的末尾。

sort() 方法保留空槽。如果源数组是稀疏的,则空槽会被移动到数组的末尾,并始终排在所有 undefined 元素的后面。

如果提供了 compareFn,所有非 undefined 的数组元素都会按照比较函数的返回值进行排序(所有的 undefined 元素都会被排序到数组的末尾,并且不调用 compareFn)。

比较函数形式如下:

function compareFn(a, b) {
  if (根据排序标准,a 小于 b) {
    return -1;
  }
  if (根据排序标准,a 大于 b) {
    return 1;
  }
  // a 一定等于 b
  return 0;
}

更正式地说,为了确保正确的排序行为,比较函数应具有以下属性:

  • 纯函数:比较函数不会改变被比较的对象或任何外部状态。(这很重要,因为无法保证比较函数将在何时以及如何调用,因此任何特定的调用都不应对外部产生可见的效果。)
  • 稳定性:比较函数对于相同的输入对应始终返回相同的结果。
  • 自反性:compareFn(a, a) === 0。
  • 反对称性:compareFn(a, b) 和 compareFn(b, a) 必须都是 0 或者具有相反的符号。
  • 传递性:如果 compareFn(a, b) 和 compareFn(b, c) 都是正数、零或负数,则 compareFn(a, c) 的符号与前面两个相同。

符合上述限制的比较函数将始终能够返回 1、0 和 -1 中的任意一个,或者始终返回 0。例如,如果比较函数只返回 1 和 0,或者只返回 0 和 -1,它将无法可靠地排序,因为反对称性被破坏了。一个总是返回 0 的比较函数将不会改变数组,但仍然是可靠的。

默认的字典比较函数符合上述所有限制。

要比较数字而非字符串,比较函数可以简单的用 a 减 b,如下的函数将会将数组升序排列(如果它不包含 Infinity 和 NaN):

function compareNumbers(a, b) {
  return a - b;
}

sort() 方法是通用的,它只期望 this 值具有 length 属性和整数键属性。虽然字符串也类似于数组,但此方法不适用于字符串,因为字符串是不可变的。

使用 sort()
function compareNumbers(a, b) {
  return a - b
}

const stringArray = ['Blue', 'Humpback', 'Beluga']
let res = stringArray.join()
console.log(res)
// 'Blue,Humpback,Beluga'
res = stringArray.sort()
console.log(res)
console.log(stringArray)
// ['Beluga', 'Blue', 'Humpback']

const numberArray = [40, 1, 5, 200]
let res1 = numberArray.join()
console.log(res1)
// '40,1,5,200'
numberArray.sort()
console.log(numberArray)
// [1, 200, 40, 5]
numberArray.sort(compareNumbers)
console.log(numberArray)
// [1, 5, 40, 200]

const numericStringArray = ['80', '9', '700']
let res2 = numericStringArray.join()
console.log(res2)
// '80,9,700'
numericStringArray.sort()
console.log(numericStringArray)
// ['700', '80', '9']
numericStringArray.sort(compareNumbers)
console.log(numericStringArray)
// ['9', '80', '700']

const mixedNumericArray = ['80', '9', '700', 40, 1, 5, 200]
let res3 = mixedNumericArray.join()
console.log(res3)
// '80,9,700,40,1,5,200'
mixedNumericArray.sort()
console.log(mixedNumericArray)
// [1, 200, 40, 5, '700', '80', '9']
mixedNumericArray.sort(compareNumbers)
console.log(mixedNumericArray)
// [1, 5, '9', 40, '80', 200, '700']
对象数组的排序

对象数组可以通过比较它们的某个属性的值来排序。

const items = [
  { name: 'Edward', value: 21 },
  { name: 'Sharpe', value: 37 },
  { name: 'And', value: 45 },
  { name: 'The', value: -12 },
  { name: 'Magnetic', value: 13 },
  { name: 'Zeros', value: 37 }
]

// 根据 value 排序
items.sort((a, b) => a.value - b.value)
console.log(items)
// [
//   {name: 'The', value: -12},
//   {name: 'Magnetic', value: 13},
//   {name: 'Edward', value: 21},
//   {name: 'Sharpe', value: 37},
//   {name: 'Zeros', value: 37},
//   {name: 'And', value: 45}
// ]

// 根据 name 排序
items.sort((a, b) => {
  // 忽略大小写
  const nameA = a.name.toUpperCase()
  // 忽略大小写
  const nameB = b.name.toUpperCase()
  if (nameA < nameB) {
    return -1
  }
  if (nameA > nameB) {
    return 1
  }

  // name 必须相等
  return 0
})
console.log(items)
// [
//   {name: 'And', value: 45},
//   {name: 'Edward', value: 21},
//   {name: 'Magnetic', value: 13},
//   {name: 'Sharpe', value: 37},
//   {name: 'The', value: -12},
//   {name: 'Zeros', value: 37}
// ]
对非 ASCII 字符排序

当排序非 ASCII 字符的字符串(如包含类似 e、é、è、a、ä 等字符的字符串)。一些非英语语言的字符串需要使用 String.localeCompare。这个函数可以将函数排序到正确的顺序。

var items1 = ['réservé', 'premier', 'cliché', 'communiqué', 'café', 'adieu']
items1.sort(function (a, b) {
  return a.localeCompare(b)
})

console.log(items1)
//['adieu', 'café', 'cliché', 'communiqué', 'premier', 'réservé']
使用 map 改善排序

compareFn 可能会在数组中的每个元素上调用多次。根据 compareFn 的性质,这可能会产生很高的开销。如果 compareFn 执行的工作更多,需要排序的元素更多,使用 map() 进行排序可能更有效率。其思路是遍历数组一次,将用于排序的实际值提取到一个临时数组中,对临时数组进行排序,然后遍历临时数组以获得正确的顺序。

// 需要被排序的数组
const data = ['delta', 'alpha', 'charlie', 'bravo']

// 用于存放位置和排序值的对象数组
const mapped = data.map((v, i) => {
  return { i, value: v }
})

// 按照多个值排序数组
mapped.sort((a, b) => {
  if (a.value > b.value) {
    return 1
  }
  if (a.value < b.value) {
    return -1
  }
  return 0
})

const result = mapped.map(v => data[v.i])
console.log(result)
// ['alpha', 'bravo', 'charlie', 'delta']
sort() 方法返回对同一数组的引用

sort() 方法返回对原始数组的引用,因此更改返回的数组将同时更改原始数组。

const numbers = [3, 1, 4, 1, 5]
const sorted = numbers.sort((a, b) => a - b)
// numbers 和 sorted 都是 [1, 1, 3, 4, 5]
sorted[0] = 10
console.log(numbers)
// [10, 1, 3, 4, 5]

如果你希望 sort() 方法不会改变原始数组,而是返回一个类似于其他数组方法(如 map() )返回的浅拷贝数组,可以使用 toSorted() 方法。或者,你可以在调用 sort() 之前使用展开语法或 Array.from() 进行浅拷贝。

const numbers2 = [3, 1, 4, 1, 5]
// [...numbers2] 创建一个浅拷贝,因此 sort() 不会改变原始数组。
const sorted2 = [...numbers2].sort((a, b) => a - b)
sorted2[0] = 10
console.log(numbers2)
// [3, 1, 4, 1, 5]
在稀疏数组上使用 sort()

空槽会被移动到数组的末尾。

console.log(['a', 'c', , 'b'].sort())
// ['a', 'b', 'c', empty]
console.log([, undefined, 'a', 'b'].sort())
// ["a", "b", undefined, empty]
在类数组对象上调用 sort()

sort() 方法会读取 this 的 length 属性。然后它会收集在 0 到 length - 1 范围内所有已存在的整数键属性,对它们进行排序,然后写回。如果范围内存在缺失的属性,则相应的尾随属性将被删除,好像不存在的属性被排序到末尾一样。

const arrayLike = {
  length: 3,
  unrelated: 'foo',
  0: 5,
  2: 4
}
console.log(Array.prototype.sort.call(arrayLike))
// { '0': 4, '1': 5, length: 3, unrelated: 'foo' }

Array.prototype.splice()

splice() 方法通过移除或者替换已存在的元素和/或添加新元素就地改变一个数组的内容。

要创建一个删除和/或替换部分内容而不改变原数组的新数组,请使用 toSpliced()。要访问数组的一部分而不修改它,参见 slice()。

splice() 方法是一个修改方法。它可能会更改 this 的内容。如果指定的要插入的元素数量与要删除的元素数量不同,数组的 length 也将会更改。同时,它会使用 @@species 来创建一个新数组实例并返回。

如果删除的部分是稀疏的,则 splice() 返回的数组也是稀疏的,对应的索引为空槽。

splice() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。尽管字符串也类似于数组,但这种方法不适用于它,因为字符串是不可变的。

使用 splice()
// 移除索引 2 之前的 0(零)个元素,并插入“drum”
const myFish = ['angel', 'clown', 'mandarin', 'sturgeon']
const removed = myFish.splice(2, 0, 'drum')
console.log(myFish)
// ["angel", "clown", "drum", "mandarin", "sturgeon"]
console.log(removed)
// []

// 移除索引 2 之前的 0(零)个元素,并插入“drum”和“guitar”
const myFish2 = ['angel', 'clown', 'mandarin', 'sturgeon']
const removed2 = myFish2.splice(2, 0, 'drum', 'guitar')
console.log(myFish2)
// ['angel', 'clown', 'drum', 'guitar', 'mandarin', 'sturgeon']
console.log(removed2)
// []

// 在索引 3 处移除 1 个元素
const myFish3 = ['angel', 'clown', 'drum', 'mandarin', 'sturgeon']
const removed3 = myFish3.splice(3, 1)
console.log(myFish3)
// ['angel', 'clown', 'drum', 'sturgeon']
console.log(removed3)
// ['mandarin']

// 在索引 2 处移除 1 个元素,并插入“trumpet”
const myFish4 = ['angel', 'clown', 'drum', 'sturgeon']
const removed4 = myFish4.splice(2, 1, 'trumpet')
console.log(myFish4)
//  ['angel', 'clown', 'trumpet', 'sturgeon']
console.log(removed4)
// ['drum']

// 从索引 0 处移除 2 个元素,并插入“parrot”、“anemone”和“blue”
const myFish5 = ['angel', 'clown', 'trumpet', 'sturgeon']
const removed5 = myFish5.splice(0, 2, 'parrot', 'anemone', 'blue')
console.log(myFish5)
// ['parrot', 'anemone', 'blue', 'trumpet', 'sturgeon']
console.log(removed5)
// ['angel', 'clown']

// 从索引 2 处开始移除 2 个元素
const myFish6 = ['parrot', 'anemone', 'blue', 'trumpet', 'sturgeon']
const remove6 = myFish6.splice(2, 2)
console.log(myFish6)
// ['parrot', 'anemone', 'sturgeon']
console.log(remove6)
// ["blue", "trumpet"]

// 从索引 -2 处移除 1 个元素
const myFish7 = ['angel', 'clown', 'mandarin', 'sturgeon']
const removed7 = myFish7.splice(-2, 1)
console.log(myFish7)
// ['angel', 'clown', 'sturgeon']
console.log(removed7)
// ['mandarin']

// 从索引 2 开始删除所有元素
const myFish8 = ['angel', 'clown', 'mandarin', 'sturgeon']
const removed8 = myFish8.splice(2)
console.log(myFish8)
// ["angel", "clown"]
console.log(removed8)
// ["mandarin", "sturgeon"]
在稀疏数组中使用 splice()

splice() 方法保留了数组的稀疏性。

const arr = [1, , 3, 4, , 6]
console.log(arr.splice(1, 2))
// [empty, 3]
console.log(arr)
// [1, 4, empty, 6]
在非数组对象中使用 splice()

splice() 方法读取 this 的 length 属性。然后,它根据需要更新整数键属性和 length 属性。

const arrayLike = {
  length: 3,
  unrelated: 'foo',
  0: 5,
  2: 4
}
console.log(Array.prototype.splice.call(arrayLike, 0, 1, 2, 3))
// [ 5 ]
console.log(arrayLike)
// { '0': 2, '1': 3, '3': 4, length: 4, unrelated: 'foo' }

Array.prototype.toLocaleString()

toLocaleString() 方法返回一个字符串,表示数组中的所有元素。每个元素通过调用它们自己的 toLocaleString 方法转换为字符串,并且使用特定于语言环境的字符串(例如逗号“,”)分隔开。

Array.prototype.toLocaleString 方法遍历数组内容,并使用提供的 locales 和 options 参数调用每个元素的 toLocaleString 方法,通过由实现定义的分隔符(例如逗号“,”)将转换后的字符串拼接起来。请注意,该方法本身不会使用这两个参数——它只是将其传递给每个元素的 toLocaleString()。分隔符的选择取决于主机当前的语言环境,而不是 locales 参数。

如果一个元素是 undefined、null,它会被转换为空字符串,而不是 “null” 或者 “undefined”。

当用于稀疏数组时,toLocaleString() 方法迭代时会把空槽当作 undefined 一样处理它。

toLocaleString() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。

toString()方法与toLocalString()方法区别
  • toLocalString()是调用每个数组元素的 toLocaleString() 方法,然后使用地区特定的分隔符把生成的字符串连接起来,形成一个字符串。

  • toString()方法获取的是String(传统字符串),而toLocaleString()方法获取的是LocaleString(本地环境字符串)。

  • 如果你开发的脚本在世界范围都有人使用,那么将对象转换成字符串时请使用toString()方法来完成。

  • LocaleString()会根据你机器的本地环境来返回字符串,它和toString()返回的值在不同的本地环境下使用的符号会有微妙的变化。

  • 所以使用toString()是保险的,返回唯一值的方法,它不会因为本地环境的改变而发生变化。如果是为了返回时间类型的数据,推荐使用LocaleString()。若是在后台处理字符串,请务必使用toString()。

使用 toLocaleString()

使用 locales 和 options
数组元素通过使用它们的 toLocaleString 方法转换为字符串。

  • Object:Object.prototype.toLocaleString()
  • Number:Number.prototype.toLocaleString()
  • Date:Date.prototype.toLocaleString()
    始终显示 prices 数组中字符串和数字的货币符号:
const prices = ['¥7', 500, 8123, 12]
let res = prices.toLocaleString('ja-JP', { style: 'currency', currency: 'JPY' })
console.log(prices)
// ['¥7', 500, 8123, 12]
console.log(res)
// "¥7,¥500,¥8,123,¥12"

//如果不传,默认:分隔符的选择取决于主机当前的语言环境
let res1 = prices.toLocaleString()
console.log(res1)
// ¥7,500,8,123,12

const list2 = [1, 'a', new Date('21 Dec 1997 14:12:00 UTC')]
const res2 = list2.toLocaleString('en', { timeZone: 'UTC' })
console.log(res2)
// 1,a,12/21/1997, 2:12:00 PM
在稀疏数组中使用 toLocaleString()

toLocaleString() 将空槽视为 undefined ,并生成一个额外的分隔符:

console.log([1, , 3].toLocaleString())
// '1,,3'
在非数组对象中使用 toLocaleString()

toLocaleString() 方法读取 this 的 length 属性,然后访问每个整数索引。

const arrayLike = {
  length: 3,
  0: 1,
  1: 2,
  2: 3
}
console.log(Array.prototype.toLocaleString.call(arrayLike))
// 1,2,3

Array.prototype.toReversed()

Array 实例的 toReversed() 方法是 reverse() 方法对应的复制版本。它返回一个元素顺序相反的新数组。

toReversed() 方法将调用该方法的数组对象的元素以相反的顺序调换,并返回一个新数组。

当用于稀疏数组时,toReversed() 方法迭代空槽,就像它们的值是 undefined 一样。

toReversed() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。

使用 toReversed()
const items = [1, 2, 3]
const reversedItems = items.toReversed()
console.log(reversedItems)
// [3, 2, 1]
console.log(items)
// [1, 2, 3]
在稀疏数组上使用 toReversed()

toReversed() 的返回值永远不是稀疏的。空槽在返回的数组中变为 undefined。

console.log([1, , 3].toReversed())
// [3, undefined, 1]
console.log([1, , 3, 4].toReversed())
// [4, 3, undefined, 1]
在非数组对象上调用 toReversed()

toReversed() 方法读取 this 的 length 属性。然后按降序访问 length - 1 和 0 之间的每个索引,并将原始数组中该索引的值添加到新数组相应的索引中。

const arrayLike = {
  length: 3,
  unrelated: 'foo',
  2: 4
}
console.log(Array.prototype.toReversed.call(arrayLike))
// [4, undefined, undefined]

Array.prototype.toSorted()

toSorted() 方法是 sort() 方法的复制方法版本。它返回一个新数组,其元素按指定的定义排序顺序的函数排列, 其元素默认将数组元素转换为字符串,然后根据每个字符的 Unicode 码位值进行排序。

有关 compareFn 参数的更多信息,请参阅 sort()。

当在稀疏数组上使用 toSorted() 方法时,它迭代时会将空槽视为具有 undefined 值的元素。

toSorted() 方法是通用的,它只期望 this 值具有 length 属性和整数键属性。

使用 toSorted()
const months = ['Mar', 'Jan', 'Feb', 'Dec']
const sortedMonths = months.toSorted()
console.log(sortedMonths)
// ['Dec', 'Feb', 'Jan', 'Mar']
console.log(months)
// ['Mar', 'Jan', 'Feb', 'Dec']

const values = [1, 10, 21, 2]
const sortedValues = values.toSorted((a, b) => a - b)
console.log(values)
// [1, 10, 21, 2]
console.log(sortedValues)
// [1, 2, 10, 21]
const sortedValues1 = values.toSorted((a, b) => b - a)
console.log(sortedValues1)
// [21, 10, 2, 1]
在稀疏数组上使用 toSorted()

空槽被视为具有 undefined 值而被排序。它们总是排序到数组的末尾,并且 compareFn 不会对它们进行调用。

console.log(['a', 'c', , 'b'].toSorted())
// ['a', 'b', 'c', undefined]
console.log([, undefined, 'a', 'b'].toSorted())
// ["a", "b", undefined, undefined]
在非数组对象上调用 toSorted()

toSorted() 方法会读取 this 的 length 属性。然后它会收集所有在 0 到 length - 1 范围内的整数键属性,对它们进行排序并将它们写入一个新的数组中。

const arrayLike = {
  length: 3,
  unrelated: 'foo',
  0: 5,
  2: 4
}
console.log(Array.prototype.toSorted.call(arrayLike))
// [4, 5, undefined]

Array.prototype.toSpliced()

toSpliced() 方法是 splice() 方法的复制版本。它返回一个新数组,并在给定的索引处删除和/或替换了一些元素。

toSpliced() 方法与 splice() 类似,可以同时完成多个操作:在数组中给定的索引开始移除指定数量的元素,然后在相同的索引处插入给定的元素。但是,它返回一个新数组,而不是修改原始数组。因此,此方法不会返回已删除的元素。

toSpliced() 方法不会产生稀疏数组。如果原始数组是稀疏的,在新数组中空槽将会被替换成 undefined。

toSpliced() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。

使用 toSpliced()
const months = ['Jan', 'Mar', 'Apr', 'May']

// 在索引 1 处添加一个元素
const months2 = months.toSpliced(1, 0, 'Feb')
console.log(months2)
// ["Jan", "Feb", "Mar", "Apr", "May"]

// 从第 2 个索引开始删除两个元素
const months3 = months2.toSpliced(2, 2)
console.log(months3)
// ["Jan", "Feb", "May"]

// 在索引 1 处用两个新元素替换一个元素
const months4 = months3.toSpliced(1, 1, 'Feb', 'Mar')
console.log(months4)
// ["Jan", "Feb", "Mar", "May"]

// 原数组不会被修改
console.log(months)
// ["Jan", "Mar", "Apr", "May"]
在稀疏数组上使用 toSpliced()

toSpliced() 方法总是会生成一个密集的数组。

const arr = [1, , 3, 4, , 6]
console.log(arr.toSpliced(1, 2))
// [1, 4, undefined, 6]
对非数组对象调用 toSpliced()

toSpliced() 方法将会读取 this 的 length 属性。然后,它读取所需的整数键属性并将其写入新数组。

const arrayLike = {
  length: 3,
  unrelated: 'foo',
  0: 5,
  2: 4
}
console.log(Array.prototype.toSpliced.call(arrayLike, 0, 1, 2, 3))
// [2, 3, undefined, 4]

Array.prototype.toString()

toString() 返回包含所有数组值的字符串,以逗号分隔。 不会改变原始数组。

Array 对象覆盖了 Object 的 toString 方法。数组的 toString 方法实际上在内部调用了 join() 方法来拼接数组并返回一个包含所有数组元素的字符串,元素之间用逗号分隔。如果 join 方法不可用或者不是函数,则会使用 Object.prototype.toString 来代替,并返回 [object Array]。

const arr = []
// 将 `join` 重新赋值为非函数的值
arr.join = 1
console.log(arr.toString())
// [object Array]
console.log(Array.prototype.toString.call({ join: () => 1 }))
// 1

当数组需要被表示为文本值,或者当数组在字符串拼接中被引用时,JavaScript 会自动调用 toString() 方法。

使用 toString()
const array1 = [1, 2, 'a', '1a']
console.log(array1)
// [1, 2, 'a', '1a']
console.log(array1.toString())
// 1,2,a,1a
在稀疏数组中使用 toString()

与 join() 的行为一致,toString() 将空槽视为 undefined 并生成一个额外的分隔符:

console.log([1, , 3].toString())
// 1,,3
在非数组对象中使用 toString()

toString() 是通用的。它期望 this 具有 join() 方法;如果不存在,则使用 Object.prototype.toString()。

console.log(Array.prototype.toString.call({ join: () => 1 }))
// 1
console.log(Array.prototype.toString.call({ join: () => undefined }))
// undefined
console.log(Array.prototype.toString.call({ join: 'not function' }))
// [object Object]

Array.prototype.unshift()

unshift() 将指定元素添加到数组的开头,并返回数组的新长度。

unshift() 方法将给定的值插入到类数组对象的开头。

Array.prototype.push() 有着和 unshift() 相似的行为,但是其将元素插入到数组的末尾。

请注意,如果多个元素作为参数传递,它们将被插入到对象开头的块中,与它们作为参数传递的顺序完全相同。因此,调用一次 unshift() 方法并传递 n 个参数,与调用 n 次并传递 1 个参数(例如使用循环),不会产生相同的结果。

unshift() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。虽然字符串也是类似于数组的,但这个方法不适用于它们,因为字符串是不可变的。

使用 unshift()
let arr = [4, 5, 6]
let res = arr.unshift(1, 2, 3)
console.log(arr)
// [1, 2, 3, 4, 5, 6]
console.log(res)
// 6

// 重置数组
arr = [4, 5, 6]
res = arr.unshift(1)
res = arr.unshift(2)
res = arr.unshift(3)

console.log(arr)
// [3, 2, 1, 4, 5, 6]
console.log(res)
// 6
在非数组对象中使用 unshift()

unshift 方法会读取 this 的 length 属性。然后,它将 0 到 length - 1 范围内的所有属性按参数数量右移,并将每个索引从 0 开始,并将参数传递给 unshift()。最后,它将 length 设置为之前的长度加上前置元素的数量。

const arrayLike = {
  length: 3,
  unrelated: 'foo',
  2: 4
}
Array.prototype.unshift.call(arrayLike, 1, 2)
console.log(arrayLike)
// { '0': 1, '1': 2, '4': 4, length: 5, unrelated: 'foo' }

const plainObj = {}
// 这里没有长度属性,所以这里的长的为 0
Array.prototype.unshift.call(plainObj, 1, 2)
console.log(plainObj)
// { '0': 1, '1': 2, length: 2 }

Array.prototype.values()

values() 返回一个包含数组所有值的迭代器对象。

Array.prototype.values() 是 Array.prototype@@iterator 的默认实现。

console.log(Array.prototype.values === Array.prototype[Symbol.iterator])
// true

当应用于稀疏数组时,values() 方法会将空槽作为 undefined 迭代。

values() 方法是通用的。它只需要 this 值具有 length 属性和整数键属性。

使用 values()
使用 for…of 循环进行迭代
// 由于 values() 返回一个可迭代迭代器对象,你可以使用 for...of 循环来迭代它
const arr = ['a', 'b', 'c', 'd', 'e']
const iterator = arr.values()
for (const letter of iterator) {
  console.log(letter)
}
// a
// b
// c
// d
// e
使用 next() 迭代
// 由于返回值也是一个迭代器,你可以直接调用其 next() 方法。
const arr1 = ['a', 'b', 'c', 'd', 'e']
const iterator1 = arr1.values()
console.log(iterator1.next())
// { value: "a", done: false }
console.log(iterator1.next())
// { value: "b", done: false }
console.log(iterator1.next())
// { value: "c", done: false }
console.log(iterator1.next())
// { value: "d", done: false }
console.log(iterator1.next())
// { value: "e", done: false }
console.log(iterator1.next())
// { value: undefined, done: true }
console.log(iterator1.next().value)
// undefined
重复使用可迭代对象

values() 返回的可迭代对象是不可重复使用的。当 next().done = true 或 currentIndex > length 时,for…of 循环结束,进一步迭代它没有任何效果。

const arr3 = ['a', 'b', 'c', 'd', 'e']
const values3 = arr3.values()
for (const letter of values3) {
  console.log(letter)
}
// a
// b
// c
// d
// e
for (const letter of values3) {
  console.log(letter)
}
//

// 如果使用 break 语句提前结束迭代,当继续迭代时,迭代器可以从当前位置恢复迭代。
const arr4 = ['a', 'b', 'c', 'd', 'e']
const values4 = arr4.values()
for (const letter of values4) {
  console.log(letter)
  if (letter === 'b') {
    break
  }
}
// a
// b
for (const letter of values4) {
  console.log(letter)
}
// c
// d
// e
迭代期间的修改操作

values() 返回的数组迭代器对象中没有存储任何值;但是它存储了用于创建它的数组的地址,并在每次迭代中读取当前访问的索引。因此,它的迭代输出取决于在迭代时存储在该索引中的值。如果数组中的值发生了改变,数组迭代器对象的值也会改变。

const arr5 = ['a', 'b', 'c', 'd', 'e']
const iterator5 = arr5.values()
console.log(iterator5)
// Array Iterator { }
console.log(iterator5.next().value)
// a
arr5[1] = 'n'
console.log(iterator5.next().value)
// n
迭代稀疏数组

values() 会访问空槽并将其视为 undefined。

for (const element of [, 'a'].values()) {
  console.log(element)
}
// undefined
// a
在非数组对象上调用 values()

values() 方法读取 this 的 length 属性,然后访问每个整数索引。

const arrayLike = {
  length: 3,
  0: 'a',
  1: 'b',
  2: 'c'
}
for (const entry of Array.prototype.values.call(arrayLike)) {
  console.log(entry)
}
// a
// b
// c

Array.prototype.with()

with() 是使用方括号表示法修改指定索引值的复制方法版本。它会返回一个新数组,其指定索引处的值会被新值替换。

with() 通过返回一个指定索引处的值被新值替换的新数组,来改变数组中指定索引处的值。原数组不会被修改。这使得你可以以链式调用数组方法的方式来对数组进行操作。

with() 方法永远不会产生稀疏数组。如果原数组是稀疏的,新数组对应的空白索引位置会替换为 undefined。

with() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。

使用 with()
const arr = [1, 2, 3, 4, 5]
console.log(arr.with(2, 6))
// [1, 2, 6, 4, 5]
console.log(arr)
// [1, 2, 3, 4, 5]

// 使用 with() 方法,你可以在更新一个数组元素后继续调用其他的数组方法。
const arr1 = [1, 2, 3, 4, 5]
console.log(arr1.with(2, 6).map(x => x ** 2))
// [1, 4, 36, 16, 25]

// index > array.length 或 index < -array.length 时抛出异常。
const list = [1, 2, 3, 4, 5]
console.log(list.with(-6, 6))
// Uncaught RangeError: Invalid index : -6
console.log(list.with(7, 6))
// Uncaught RangeError: Invalid index : 7
在稀疏数组上使用 with()

with() 方法总会创建一个密集数组。

const arr2 = [1, , 3, 4, , 6]
console.log(arr2.with(0, 2))
// [2, undefined, 3, 4, undefined, 6]
在非数组对象上调用 with()

with() 方法会读取 this 上的 length 属性,之后读取 this 上的每个整数键并写入到新数组中,同时 value 会被写入指定的 index。

const arrayLike = {
  length: 3,
  unrelated: 'foo',
  0: 5,
  2: 4
}
console.log(Array.prototype.with.call(arrayLike, 0, 1))
// [ 1, undefined, 4 ]

hasOwnProperty()

hasOwnProperty() 方法是 Object 的原型方法(也称实例方法),它定义在 Object.prototype 对象之上,所有 Object 的实例对象都会继承 hasOwnProperty() 方法。

hasOwnProperty() 方法用来检测一个属性是否是对象的自有属性,而不是从原型链继承的。如果该属性是自有属性,那么返回 true,否则返回 false。换句话说,hasOwnProperty() 方法不会检测对象的原型链,只会检测当前对象本身,只有当前对象本身存在该属性时才返回 true。

所有继承了 Object 的对象都会继承到 hasOwnProperty 方法。这个方法可以用来检测一个对象是否含有特定的自身属性;和 in 运算符不同,该方法会忽略掉那些从原型链上继承到的属性。

即使属性的值是 null 或 undefined,只要属性存在,hasOwnProperty 依旧会返回 true。

JavaScript 并没有保护 hasOwnProperty 这个属性名,因此,当某个对象可能自有一个占用该属性名的属性时,就需要使用外部的 hasOwnProperty 获得正确的结果:

使用 hasOwnProperty()
let arr = [1, 2]
let res = arr.hasOwnProperty(1)
console.log(res)
// true
res = arr.hasOwnProperty(0)
console.log(res)
// true
res = arr.hasOwnProperty(2)
console.log(res)
// false
res = arr.hasOwnProperty('toString')
console.log(res)
// false

Array.prototype.name = '继承属性'
res = arr.hasOwnProperty('name')
console.log(arr.name)
// 继承属性
console.log(res)
// false

// 使用 hasOwnProperty 作为属性名
arr.hasOwnProperty = function () {
  return false
}
res = arr.hasOwnProperty(0)
console.log(res)
// false

// 如果担心这种情况,
// 可以直接使用原型链上真正的 hasOwnProperty 方法
res = [].hasOwnProperty.call(arr, 0)
console.log(res)
// true

// 也可以使用 Object 原型上的 hasOwnProperty 属性
res = Object.prototype.hasOwnProperty.call(arr, 0)
console.log(res)
// true

isPrototypeOf()

isPrototypeOf()函数用于指示对象是否存在于另一个对象的原型链中。如果存在,返回true,否则返回false。

该方法属于Object对象,由于所有的对象都"继承"了Object的对象实例,因此几乎所有的实例对象都可以使用该方法。

利用isPrototypeOf()方法,判定Array是不是在obj的原型链中,如果是,返回true,否则返回false。

使用 isPrototypeOf()
console.log(Array.prototype.isPrototypeOf([1, 2, 3]))
//true
console.log(Array.prototype.isPrototypeOf([]))
//true
console.log(Array.prototype.isPrototypeOf({}))
//false
console.log(Array.prototype.isPrototypeOf(undefined))
//false
console.log(Array.prototype.isPrototypeOf(0))
//false
console.log(Array.prototype.isPrototypeOf(false))
//false
console.log(Array.prototype.isPrototypeOf(true))
//false
console.log(Array.prototype.isPrototypeOf(''))
//false
console.log(
  Array.prototype.isPrototypeOf(function name(params) {
    console.log(params)
  })
)
//false
console.log(Array.prototype.isPrototypeOf(new Date()))
//false

propertyIsEnumerable()

propertyIsEnumerable()方法返回布尔值,该值指示指定属性是否为对象的一部分以及该属性是否是可枚举的。

该方法属于Object对象,由于JS中的所有对象都是Object的实例,因此几乎所有的实例对象都可以使用该方法。所有主流浏览器均支持该方法。

每个对象都有一个 propertyIsEnumerable 方法。此方法可以确定对象中指定的属性是否可以被 for…in 循环枚举,但是通过原型链继承的属性除外。如果对象没有指定的属性,则此方法返回 false。

使用 propertyIsEnumerable()
// propertyIsEnumerable 方法在普通对象和数组上的基本用法:
var o = {}
var a = []
o.prop = 'is enumerable'
a[0] = 'is enumerable'
console.log(o.propertyIsEnumerable('prop'))
// true
console.log(a.propertyIsEnumerable(0))
// true

// 用户自定义对象和内置对象上属性可枚举性的区别
var a = ['is enumerable']
console.log(a.propertyIsEnumerable(0))
// true
console.log(a.propertyIsEnumerable('length'))
// false
console.log(Math.propertyIsEnumerable('random'))
// false
console.log(this.propertyIsEnumerable('Math'))
// false

// 自身属性和继承属性
var o = {}
var a = []
o.prop = 'is enumerable'
a[0] = 'is enumerable'
console.log(o.propertyIsEnumerable('prop'))
// true
console.log(a.propertyIsEnumerable(0))
// true

// 用户自定义对象和内置对象上属性可枚举性的区别
var a1 = ['is enumerable']
console.log(a1.propertyIsEnumerable(0))
// true
console.log(a1.propertyIsEnumerable('length'))
// false
console.log(Math.propertyIsEnumerable('random'))
// false
console.log(this.propertyIsEnumerable('Math'))
// false

// 自身属性和继承属性
var a2 = []
console.log(a2.propertyIsEnumerable('constructor'))
// false

function firstConstructor() {
  this.property = 'is not enumerable'
}
firstConstructor.prototype.firstMethod = function () {}

function secondConstructor() {
  this.method = function method() {
    return 'is enumerable'
  }
}
secondConstructor.prototype = new firstConstructor()
secondConstructor.prototype.constructor = secondConstructor

var o1 = new secondConstructor()
o1.arbitraryProperty = 'is enumerable'
console.log(o1.propertyIsEnumerable('arbitraryProperty'))
// true
console.log(o1.propertyIsEnumerable('method'))
// true
console.log(o1.propertyIsEnumerable('property'))
// false
o1.property = 'is enumerable'
console.log(o1.propertyIsEnumerable('property'))
// true

// 之所以这些会返回 false,是因为,在原型链上 propertyIsEnumerable 不被考虑
// (尽管最后两个在 for-in 循环中可以被循环出来)。
console.log(o1.propertyIsEnumerable('prototype'))
// false
console.log(o1.propertyIsEnumerable('constructor'))
// false
console.log(o1.propertyIsEnumerable('firstMethod'))
// false

valueOf()

valueOf() 方法用于返回指定对象的原始值,若对象没有原始值,则将返回对象本身。通常由JavaScript内部调用,而不是在代码中显式调用。

valueOf()是数组对象的默认方法,该方法属于Object对象,由于所有的对象都"继承"了Object的对象实例,因此几乎所有的实例对象都可以使用该方法。

valueOf()方法不会更改原始数组。

所有的对象都继承有toString() 和 valueOf() 方法,对象到字符串,对象到数字的转换,会通过调用待转换对象的这两个方法中的一个来完成。

使用 valueOf()
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May']
let res = months.valueOf()
console.log(res)
//  ['Jan', 'Feb', 'Mar', 'Apr', 'May']
console.log(months.valueOf() === months)
// true

参考资料

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array
https://blog.csdn.net/kelly0721/article/details/112005664

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值