JS中对象相关知识的总结

246 阅读4分钟

最近在复习JS的基础知识,到了对象的部分,写一篇文章来总结一下

修改对象扩展性的三个方法

JS中有三个修改对象扩展性的方法:Object.preventExtensions, Object.seal, Object.freeze,并且有与之对应的三个判断方法:Object.isExtensible, Object.isSealed, Object.isFrozen,下面总结一下

Object.preventExtensions

这个方法会让对象不可扩展,也就是不能添加新的属性,并且一旦对象变为不可扩展之后,就不能再让其恢复扩展性,改方法只会对自身属性有效,不会对原型上的属性起作用,并且该方法会让__proto__不可变,这一点需要注意。下面的代码会报错

let obj = {}
Object.preventExtensions(obj)

// 报错
obj.__proto__ = {
  a: 1
}

Object.seal

这个方法会封闭原对象,就是在Object.preventExtensions的基础上,将每个属性(包括不可枚举属性)的configurable设置为false,这样就不能删除原有属性,也不能将原有的属性设置为getter, setter

Object.freeze

这个方法会在Object.seal的基础上,将每个属性(包括不可枚举的属性)的writable设置为false,这样每个属性的值就不能改变,即便通过setter也不能改变。

一个小总结

除了前面提到的内容外,这三个方法的返回值都是源对象,也就是在源对象的基础上进行操作。此外,这三个方法对对象的操作只有一层(类似浅拷贝)

let obj = {
  a: 1,
  o: {
    b: 2
  }
}

Object.freeze(obj)

obj.o.c = 3 // 成功

一个有趣的地方:

let obj = {}
Object.preventExtensions(obj)

Object.isSealed(obj) // true
Object.isFrozen(obj) // true

对于一个空对象,禁止其扩展后,由于该对象没有任何属性,因此isSealedisFrozen也就变成了true

判断对象是否存在某个属性

in操作符

in操作符判断属性是否存在时,会在目标对象的原型上进行查找,该操作符支持不可枚举属性,支持Symbol

Object.hasOwnProperty

该方法只会查找对象自身是否有目标属性,支持不可枚举属性,支持Symbol

遍历对象的方法

对于遍历对象属性的方式,我分成了两类,ES5及之前提供的,以及ES6提供的,区分依据是是否支持遍历Symbol类型

ES5方法

for ... in ...

该方法可遍历对象自身与原型上的可枚举属性,不支持Symbol

Object.getOwnPropertyNames

该方法可遍历对象自身的所有属性,无论是否可枚举,不支持Symbol

Object.keys

这个方法平时用的应该比较多,它能够遍历对象自身的所有可枚举属性,不支持Symbol

ES6方法

Object.getOwnPropertySymbols

该方法是用来遍历Symbol类型的属性的,只能遍历对象自身无论属性是否可枚举

Object.getOwnPropertyDescriptors

我认为严格来说这个方法不能算是一个遍历对象的方法,但是它确实能够遍历对象,姑且把它也算上吧,害。

这个方法能够遍历对象自身所有属性,无论是否可枚举,支持Symbol类型的属性

Object.assign

这个方法需要注意的点比较多:

  1. 这个方法的length属性是2,尽管它能接受多个源对象
  2. 第一个参数是目标对象,不能是null或者undefined
  3. 这个方法只会拷贝可枚举的属性到目标对象上
  4. 当源对象上有getter时,这个方法会失真,只会拷贝值

一个基础版的Object.assign方法的实现

// 为了简便,没有处理这个方法的length属性
Object.assign = function(target, ...sources) {
  if (target == null) {
    throw new TypeError('Cannot convert undefined or null to object')
  }
  target = Object(target)
  for (let i = 0, len = sources.length; i < len; i++) {
    let source = sources[i]
    // 跳过null和undefined
    if (source != null) {
      // 使用Object.keys找出可枚举属性
      Object.keys(source).forEach(key => target[key] = source[key])
    }
  }
  return target
}

下面是一个支持getter, setter版本的实现

Object.assign = function(target, ...sources) {
  if (target == null) {
    throw new TypeError('Cannot convert undefined or null to object')
  }
  target = Object(target)
  for (let i = 0, len = sources.length; i < len; i++) {
    let source = sources[i]
    if (source != null) {
      const descriptors = Object.keys(source).reduce((obj, key) => {
        obj[key] = Object.getOwnPropertyDescriptor(obj, key)
        return obj
      }, {})
    }
    Object.definePropertyDescriptors(target, descriptors)
  }
  return target
}

另附一个网上看到的题:

const v1 = 123
const v2 = '123' // 自动装箱/包装类
const v3 = true
const v4 = function() {} // 函数的所有属性都不可枚举
const v5 = Object.assign({}, v1, v2, v3, v4)

console.log(v5)
// {
//   0: 1,
//   1: 2,
//   2: 3
// }