JavaScript ES(6-11)全版本语法 (五):Object

357 阅读5分钟

前提概要

上一篇编写的是ES6中的Function,链接:juejin.cn/post/700205… ,这次写的是ES6中Object的一些API和部分应用场景。如有不对的或者不准确的地方,欢迎大家提出😄,我也积极修改。下边开始正文:

e58a2a35-d0cc-422d-8d7e-0feba136a2b2.jpg

属性简洁表示法

在ES6之前Object的属性必须是key-value形式,如下:

let name = 'xs'
let age = 18
let obj = {
  name: name,
  age: age,
  study: function() {
    console.log(this.name + '正在学习')
  }
}
console.log(obj.study()) // xs正在学习

在ES6之后是可以用简写的形式来表达:

let name = 'xs'
let age = 18
let obj = {
  name,
  age,
  study() {
    console.log(this.name + '正在学习')
  }
}
console.log(obj.study()) // xs正在学习

属性名表达式

在ES6之前声明属性名如下:

let a = 'address'
let obj = {
  a: a
}
console.log(obj) // {a: "address"}

在ES6之后可以直接用变量或者表达式来定义Object的key

let a = 'address'
let obj = {
  name: 'xs',
  [a]: '北京'
}
console.log(obj) // {name: "xs", address: "北京"}

Object.is()

Object.is()判断两个对象是否严格相等

console.log(Object.is(2, '2')) // false
console.log(Object.is(NaN, NaN)) // true
console.log(Object.is(+0, -0)) // false
let obj1 = { // new Object()
  name: 'xs',
  age: 18
}
let obj2 = { // new Object()
  name: 'xs',
  age: 18
}
// 判断两个对象的引用地址是否相等
console.log(obj1 == obj2) // false
console.log(Object.is(obj1, obj2)) // false
let obj2 = obj1
console.log(Object.is(obj1, obj2)) // true

扩展运算符与Object.assign()

扩展运算符是取出参数对象中的可编制属性拷贝到当前的对象当中

let x = {
  a: 3,
  b: 4
}
let y = { ...x }
console.log(y) // {a: 3, b: 4}

Object.assign()方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。

Object.assign(target,...source)主要接收两个参数,target:目标对象,source:是源对象

let x = {
  a: 3,
  b: 4
}
let y = {}
Object.assign(y, x)
console.log(y) // {a: 3, b: 4}

Object.assign()合并对象时,source会把target目标中相同的属性替换掉

let x = {
  a: 3,
  b: 4
}
let z = { c: 5, a: 6 }
Object.assign(z, x)
console.log(z) // {{c: 5, a: 3, b: 4}}
let x = Object.assign({
    a: 1
})
console.log(x) // {a: 1}

注意: 从语法上可以看出源对象的个数是不限制的(零个或多个),如果是零个直接返回目的对象,如果是多个相同属性的会被后边的源对象的属相覆盖。

如果目的对象不是对象,则会自动转换为对象

let a = Object.assign(2)
console.log(a) // Number {2}
let b = Object.assign(2, {
    a: 2
})
console.log(b) // Number {2, a: 2}

in

in判断对象或者数组当中是否存在某个属性

let x = {
  a: 3,
  b: 4
}
// 判断对象当中是否有这个属性
console.log('aa' in x) // false
let y = [1, 2, 3]
// 判断数组的下标的位置是否有值
console.log(2 in y) // true

对象的遍历方式

  • 方法一:for...in的作用是用于遍历对象的

    let obj = {
      name: 'xs',
      age: '18',
      address: '北京'
    }
    for(let key in obj){
      console.log(key,obj[key]) // name xs  age 18  address 北京
    }
    
  • 方法二:Object.keys()用于返回对象所有key组成的数组

    let obj = {
      name: 'xs',
      age: '18',
      address: '北京'
    }
    Object.keys(obj).forEach(key => {
      console.log(key, obj[key]) // name xs  age 18  address 北京
    })
    
  • 方法三:Object.getOwnPropertyNames()用于返回对象所有key组成的数组。

    let obj = {
      name: 'xs',
      age: '18',
      address: '北京'
    }
    Object.getOwnPropertyNames(obj).forEach(key => {
      console.log(key, obj[key]) // name xs  age 18  address 北京
    })
    
  • 方法四:Reflect.ownKeys()用于返回对象所有key组成的数组。

    let obj = {
      name: 'xs',
      age: '18',
      address: '北京'
    }
    Reflect.ownKeys(obj).forEach(key => {
      console.log(key, obj[key]) // name xs  age 18  address 北京
    })
    

深拷贝和浅拷贝

对象的浅拷贝:浅拷贝是对象共用的一个内存地址,对象的变化相互印象。
对象的深拷贝:简单理解深拷贝是将对象放到新的内存中,两个对象的改变不会相互影响。

Object.assign()是否可以深拷贝呢?

let target = {
  a: {
    b: {
      c: 1
    },
    d: 4,
    e: 5,
    f: 6
  }
}
let source = {
  a: {
    b: {
      c: 1
    },
    d: 2,
    e: 3
  }
}
Object.assign(target, source)
console.log(target) // a: {b: {c: 1},d: 2,e: 3}

通过上述代码,这时打印的target并不对,我们在target中的f属性没了,所以说Object.assign()在拷贝对象的时候是有问题的,对于基本数据类型是可以的替换,但是对于引用数据类型,它只是把引用地址直接指向source的堆中的内存空间,如果target对象很复杂,而且对象内有引用数据类型,那么有些属性就会丢掉,所以使用Object.assign()去复制一个较复杂的结构时并不安全,所以Object.assign()它只是一个浅拷贝并不是深拷贝

如何实现一个深拷贝?

  • 方法一:JSON.parse()和JSON.stringify()
    JSON.parse() // 把一个JSON转换为对象
    JSON.stringify() // 把一个对象转换为JSON格式的字符串

    // 先把对象转换为JSON格式的字符串,再把JSON格式的字符串转换为JSON
    let obj1 = {
      name: 'xs',
      age: 18
    }
    let str = JSON.stringify(obj1)
    let obj2 = JSON.parse(str)
    obj1.age = 14
    console.log(obj2) // {name: "xs", age: 18}
    

    通过上述代码可以发现obj2并没有改变,所以可以得出,obj1和obj2是不同的引用地址,并且指向堆内存中的不同内存空间。

  • 方法二(只考虑对象、数组和基本数据类型的深拷贝)

    // 1. 第一步:判断数据的类型
    let checkType = data => {
      return Object.prototype.toString.call(data).slice(8, -1)
    }
    // 2. 第二步:进行拷贝
    let cloneDeep = target => {
      let type = checkType(target)
      let result
      if (type === 'Object') {
        result = {}
      } else if (type === 'Array') {
        result = []
      } else {
        return target
      }
      for (let i in target) {
        let value = target[i]
        let valueType = checkType(value)
        if (valueType === 'Object' || valueType === 'Array') {
          result[i] = cloneDeep(value) // 递归
        } else {
          result[i] = value
        }
      }
      return result
    }
    // 3. 第三步:测试
    // 数组类型
    let arr1 = [1, 2, { age: 18 }]
    let arr2 = cloneDeep(arr1)
    arr2[2].age = 12
    console.log(arr1) // [1, 2, {age:18}]
    console.log(arr2) // [1, 2, {age:12}]
    // 对象类型
    let obj1 = { name: 'xs', like: ['guitar', 'music'] }
    let obj2 = cloneDeep(obj1)
    obj2.like[0] = 'basketball'
    console.log(obj1) // name: "xs", like: ['guitar', 'music']
    console.log(obj2) // name: "xs", like: ['basketball', 'music']