《JS 数据处理 - 对象和数组》

对象

对象内容读取

如果属性键为字符串,且该字符串中只包含英文字母和下划线的话,可以直接用 <obj>.<key> 来读取属性值。

const essay = {
    title: '基本数据处理 - 对象和数组',
    content: ['对象', '数组'],
    from: {
        title: '基本数据处理',
        author: {
            name: 'tmac',
            description: '全栈开发',
            skill: [ 'Web 开发', '移动端开发', '小程序开发' ]
        }
    },
}

essay.title             // 基本数据处理 - 对象和数组
essay.from.author.name  // 阿梦

而当对象中所需要读取的目标属性键为数字、包含英文字母和下划线以外的字符串甚至是 Symbol 对象的时候,就需要使用 obj[key] 的形式来读取属性值了。

const obj = {
  1: 2,
  'a b c': 'd e f',
  [Symbol.for('foo')]: 'bar'
}

obj[1]                  //=> 2
obj['a b c']            //=> d e f
obj[Symbol.for('foo')]  //=> bar...

修改对象内容

在 JavaScript 中存在着“引用”和“值”的概念区别,当然这同样不是本书的讨论范围。简单地解释,就是对对象内容进行修改跟进行读取类似,只是在读取语句后面加上 = <new value> 即可。

const obj = {
  foo: 'bar',
  1: 2,
  'a b c': 'd e f',
  [Symbol.for('foo')]: 'bar'
}

obj.foo = 'rab'
obj[1] = 3

obj.foo // rab
obj[1]  // 3

当然,当你需要为一个对象添加新的属性时,也是通过同样的方式添加属性。

const obj = {}

obj.foo = 'bar'
obj[1] = 2

但要非常注意的是,在一般情况下,无论是对对象进行添加、读取还是修改属性,都遵循着嵌套链完整的原则,具体如下例所示。

const obj = {
  item: {}
}

obj.item.foo = 'bar'    // 正确
obj.something.bar = 1   // 错误

处理对象的一些常见方法

1. 对象的深拷贝

调用JSON.parse(JSON.stringify(obj))来实现对象obj的复制,也可以使用 Object.assign(target, …sources) 来拷贝

let obj = { a: 'apple' }
let objCopy1 = JSON.parse(JSON.stringify(obj)
let objCopy2 = Object.assign({}, obj)
let objCopy3 = {...obj}

obj.a = 'banana'

obj.a       // 'banana'
objCopy1.a  // 'apple'
objCopy2.a  // 'apple'
objCopy3.a  // 'apple'

2. 对象的合并

ES5 中增加了原生的 Object.assign(target, …sources) 来实现合并。而利用 ES6 中的扩展运算符,调用形如{…x, …y} 的声明,也能实现对象的合并。

let obj1 = { a: 'apple' }
let obj2 = { b: 'banana' }
let obj3 = { c: 'orange' }

Object.assign(obj1, obj2 , obj3)
Object.assign({}, obj1, obj2 , obj3)
{...obj1, ...obj2, ...obj3}

// {a: "apple", b: "banana", c: "orange"}

3. Object.keys(obj) 的用法

const arr = ['a', 'b', 'c']
Object.keys(arr) // ['0', '1', '2']


const obj = { 0: 'a', 1: 'b', 2: 'c' };
Object.keys(obj) // ['0', '1', '2']


const obj2 = { name: 'tony', sex: 'male', age: 25 };
Object.keys(obj2) // ["name", "sex", "age"]


const anObj = { 100: 'a', 2: 'b', 7: 'c' };
Object.keys(anObj) // ['2', '7', '100']


const myObj = Object.create({}, {
    getFoo: {
        value: () => this.foo
    } 
})
myObj.foo = 1
console.log(Object.keys(myObj)) // ['foo']

也可用于 Vue 中将组件批量挂载到全局

const components = {
    AppHeader,
    AppBody,
    AppFooter
}

const install = (Vue) => {
    Object.keys(components).forEach((key) => {
        Vue.component(key, components[key])
    })
}

Vue.use({...components, install})

4. Object.entries() 的用法

Object.entries() 方法返回一个给定对象自身可枚举属性的键值对数组

const object1 = { foo: 'bar', baz: 42 }
Object.entries(object1)[1] // ["baz", 42]


const object2 = { 0: 'a', 1: 'b', 2: 'c' }
Object.entries(object2)[2] // ["2", "c"]


const object3 = { 100: 'a', 2: 'b', 7: 'c' }
Object.entries(object3)[0] // ["2", "b"]
Object.entries(object3)[1] // ["7", "c"]

数组

数组类型监测

  • instanceof 只有一个全局作用域下,使用这种方法。若网页中包含多个框架,容易出错。
const arr = ["a","b","c"]
arr instanceof Array  // true
arr instanceof Object // true
  • Array.isArray() 不管有几个全局环境,都可以,但是兼容性不高。
Array.isArray([1, 2, 3])  // true
Array.isArray({foo: 123}) // false
Array.isArray("foobar")   // false
Array.isArray(undefined)  // false
  • Object.prototype.toString.call() 使用对象的 toString 函数判断,兼容性好。不能使用数组自身的 toString,因为该函数已经被重写了。
const arr = ["a","b","c"]
Object.prototype.toString.call(arr)  // "[object Array]"

处理数组的一些常用方法

1. 数组转为字符串

const arr = ["a","b","c"]

arr.toLocaleString() // "a,b,c"
arr.toString()       // "a,b,c"
arr.join(',')        // "a,b,c"
arr.valueOf()        // 返回本身 ["a", "b", "c"]
...arr               // a b c

2. 其他数据结构转为数组

  • Array.from(obj, mapFn, thisArg) 方法有一个可选参数 mapFn,让你可以在最后生成的数组上再执行一次 map 方法后再返回。也就是说 Array.from(obj, mapFn, thisArg) 就相当于 Array.from(obj).map(mapFn, thisArg)
Array.from('foo') // ["f", "o", "o"]
Array.from([1, 2, 3], x => x + x) // [2, 4, 6]
  • Array.of() 将一组值转换为数组
Array.of(3, 11, 8)  // [3,11,8]
Array.of(3)         // [3]
Array.of(3).length  // 1

3. 数组的添加和删除 (会改变原数组)

  • 栈方法 arr.push(el1, ..., elN) 将一个或N个元素添加到数组的末尾,并返回新数组的长度 ```js let arr = [“a”,“b”,“c”]

arr.push(“d”) // 返回 4,数组改变为 [“a”,“b”,“c”,“d”] arr.push(“d”,“e”) // 返回 5, [“a”,“b”,“c”,“d”,“e”]


- 栈方法 `arr.pop()` 从数组中删除最后一个元素,并返回该元素的值
```js
let arr = ["a","b","c"]

arr.pop() // 返回 "c",数组改变为 ["a","b"]
  • 队列方法 arr.shift() 从数组中删除第一个元素,并返回该元素的值 ```js let arr = [“a”,“b”,“c”]

arr.shift() // 返回 “a”,数组改变为 [“b”,“c”]


- 队列方法 `arr.unshift(el1, ..., elN)` 将一个或 N 个元素添加到数组的开头,并返回新数组的长度
```js
let arr = ["a","b","c"]

arr.unshift("-1")      // 返回 4,数组改变为 ["-1","a","b","c"]
arr.unshift("-2","-1") // 返回 5,["-2","-1","a","b","c"]

4. 数组的排序 (会改变原数组)

  • arr.reverse() 方法将数组中元素的位置颠倒 ```js let arr = [“a”,“b”,“c”]

arr.reverse() // 数组改变为 [“c”,“b”,“a”]

- `arr.sort()` 方法用原地算法对数组的元素进行排序,并返回数组。排序不一定是稳定的。默认排序顺序是根据字符串`Unicode`码点。

```js
const months = ['March', 'Jan', 'Feb', 'Dec']
months.sort() // ["Dec", "Feb", "Jan", "March"]

const numbers = [1, 30, 4, 21]
months.sort() // [1, 21, 30, 4]

5. 数组的合并

  • 扩展运算符 ...
const arr1 = ['a', 'b', 'c']
const arr2 = ['d', 'e', 'f']

[...arr1, ...arr2] // ['a', 'b', 'c', 'd', 'e', 'f']
  • arr1.concat(arr2) 合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组
const arr1 = ['a', 'b', 'c']
const arr2 = ['d', 'e', 'f']

arr1.concat(arr2) // ['a', 'b', 'c', 'd', 'e', 'f']

6. 数组的切割、截取 slice()

arr.slice(begin, end) 方法返回一个从开始到结束(不包括结束)选择的数组的一部分浅拷贝到一个新数组对象。此方法不会更改现有数组,而是返回一个新数组。

let arr = ["a","b","c","d","e"]

arr.slice(2)     // ["c","d","e"]
arr.slice(0, 2)  // ["a","b"]
arr.slice(3, 4)  // ["d"]

7. 数组的拼接 (会改变原数组)

arr.splice(start, deleteCount, items) 方法: - 第一个参数start表示起始位置 - 第二个参数deleteCount表示要删除的个数(若为0,表示不删除) - 第三个参数items表示要添加的项(可选,可以传多个) - 会改变原数组 - 该方法经常用来做前后互换的排序

let arr = ["a","b","c","d","e"]

arr.splice(1,0,"666") // ["a","666","b","c","d","e"]
arr.splice(4,1,"888") // ["a","666","b","c","d","888"]

8. 数组的填充 fill()

arr.fill(content, start, end) 方法,使用给定值,填充一个数组: - 第一个参数 content 表示填充的值 - 第二个参数 start 表示填充起始位置 - 第三个参数 end 表示填充的结束位置的前一个 - 常用于接口数据模拟

['a', 'b', 'c'].fill(7)       // [7, 7, 7]
new Array(3).fill(7)          // [7, 7, 7]
['a', 'b', 'c'].fill(7, 1, 2) // ['a', 7, 'c']

9. 查找项的位置 indexOf lastIndexOf

  • arr.indexOf(sth) 从前往后查找,返回查找项的位置,没有的话返回-1
  • arr.lastIndexOf(sth) 从后往前查找,返回查找项的位置,没有的话返回-1

10. 数组的累加 & 聚合 reduce()

arr.reduce(callback) 方法对累加器和数组中的每个元素(从左到右)应用一个函数,将其简化为单个

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

// 求和 => 15
arr.reduce((a, b) => a + b)

// 求积 => 120
arr.reduce((a, b) => a * b)

11. 数组的查找和过滤

  • arr.find(callback) 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。
const arr = [5, 12, 8, 130, 44]
arr.find((item) => item > 10) // 12
  • arr.findIndex(callback) 方法返回数组中满足提供的测试函数的第一个元素的索引index。否则返回 -1。
const arr = [5, 12, 8, 130, 44]
arr.findIndex((item) => item > 10) // 1
  • arr.some(callback) 方法测试数组中的某些元素是否通过由提供的函数实现的测试。返回Boolean值。
const arr = [5, 12, 8, 130, 44]
arr.some((item) => item > 10) // true
  • arr.every(callback) 方法测试数组的所有元素是否都通过了指定函数的测试。
const arr = [5, 12, 8, 130, 44]
arr.every((item) => item > 10) // false
  • arr.includes(value) 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。
const arr = [5, 12, 8, 130, 44]
arr.includes(5) // true
  • arr.filter(callback) 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。不更改原数组。
const arr = [5, 12, 8, 130, 44]
arr.filter((item) => item < 100) // [5, 12, 8, 44]

12. 数组的循环 forEach()

arr.forEach() 方法对数组的每个元素执行一次提供的函数

let arr = ['a', 'b', 'c']

arr.forEach((item) => {
  console.log(item);
})

// expected output: "a"
// expected output: "b"
// expected output: "c"

13. 数组的遍历 map()

arr.map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果

let arr = [
    {
        age: 14,
        name: "小红"
    },
    {
        age: 18,
        name: "小明"
    },
    {
        age: 22,
        name: "老王"
    }
]

arr.map(item => item.age*2) // [28, 36, 44]

arr.map(item => item.name) // ["小红", "小明", "老王"]