对象以及继承的总结(一)理解对象

246 阅读4分钟

什么是对象?

对象是一组属性的无序集合。每一个属性或者方法都是由一个名称标识来映射一个值。可以把对象想成一张散列表,内容就是一堆的名/值对。

对象的属性

对象的属性可以分为数据属性和访问器属性,使用了一些内部特性来描述属性的特征,通常会用两个中括号括起来,比如:[[Enumerable]]

  1. 数据属性
    • [[Configurable]]: 属性是否可以通过delete删除并重新定义、是否可以修改属性特性以及是否可以改为防问器属性,默认值为true。
    • [[Enumerable]]: 表示属性是否可以通过for-in循环返回,默认为true
    • [[Writable]]: 表示属性的值是否可以被修改,默认为true
    • [[Value]]: 包含属性实际的值,默认为undefined
  2. 访问器属性
    • [[Configurable]]: 属性是否可以通过delete删除并重新定义、是否可以修改属性特性以及是否可以改为防问器属性,默认值为true。
    • [[Enumerable]]: 表示属性是否可以通过for-in循环返回,默认为true
    • [[Get]]: 获取函数,在读取属性时调用,默认为undefined
    • [[Set]]: 设置函数,在写入属性时调用,默认为undefined

定义或修改属性

通常我们要修改属性的默认特性,就会使用Object.defineProperty()。访问器属性是不能直接定义的,必须使用 Object.defineProperty()

// 接收3个参数:要给其添加属性的对象、属性的名称、描述符对象(可以包含configurable、enumerable、writable、value)
let obj = { name : '这是一个对象' }
Object.defineProperty(obj,"name",{
    writable: false,
    value: "这是被修改后的对象"
})
console.log(obj.name) // 这是被修改后的对象
obj.name = 123
console.log(obj.name) // 这是被修改后的对象,因为writable被设置为false

如果想要定义多个属性,则可以使用Object.defineProperties(),可以通过多个描述符一次性定义多个属性,接受2个参数:添加或修改属性的对象 、 对象的描述符对象。

  let obj1 = {};
  Object.defineProperties(obj1, {
    name: {
      value: "obj1",
    },
    age1: {
      value: 18,
      configurable: true,
      writable: true,
    },
    age2: {
      get() {
        return this.age1;
      },
      set(val) {
        this.age1 += val;
      },
    },
  });
  obj1.age2 = 1;
  console.log(obj1.age2); // 19
  console.log(obj1.age1); // 19

读取属性

Object.getOwnPropertyDescriptor()可以取得指定属性的属性描述符。接收两个参数,属性所在的对象和属性名。返回一个对象,包含configurable、enumerable、数据属性则包含writable和value、访问器属性则包含get和set。 Object.getOwnPropertyDescriptors()则会在每个自有属性上调用Object.getOwnPropertyDescriptor(),并在新对象中返回他们。

  let obj2 = {
    name: "obj2",
    age: 22,
  };
  console.log(Object.getOwnPropertyDescriptor(obj2, "name")); 
  // {value: 'obj2', writable: true, enumerable: true, configurable: true}

获取对象属性

Object.keys()可以获得对象上所有可枚举的实例属性。 Object.getOwnPropertyNames()可以列出所有实例属性,无论是否可以枚举。 Object.getOwnPropertySymbols()和getOwnPropertyNames类似,但是只针对符号。

对象合并

ES6中提供了一个专门合并对象的方法Object.assign()。接受一个目标对象,和一个或多个源对象作为参数,将每个源对象中可枚举属性和自有属性复制到目标对象。复制时,实际上会使用源对象上的[[Get]]取得属性的值,然后使用目标对象上的[[Set]]设置属性的值。

    dest1 = {
      name: "123",
      set a(val) {
        this.name = val;
      },
    };
    src1 = {
      get a() {
        console.log("src1 get");
        return "foo";
      },
    };
    Object.assign(dest1, src1);
    console.log(dest1); // {name:'foo',set a(val){...}}

实际上,assign()对每个源对象执行的是浅复制,如果有多个相同的属性,则后面的属性会覆盖掉前面的那个属性值。如果赋值的过程中出错了,操作就会终止并推出,并抛出一场,之前已经复制的值会存在于目标对象中。

对象迭代

for/in:循环指定对象的每个可枚举属性。对象继承的内置方法是不可枚举的,但是添加给对象的属性默认是可枚举的。

Object.values():返回对象值的数组。

Object.entries():返回对象内容的数组,是一个键/值对的数组。当然,如果有一个键/值对数组,想要转换成对象,则可以使用Object.fromEntries()

const entries = new Map([
  ['foo', 'bar'],
  ['baz', 42]
]);
const obj = Object.fromEntries(entries);
console.log(obj); // { foo: "bar", baz: 42 }
console.log(Object.entries(obj)) // [["foo", "bar"],["baz", 42]]

Object.is()

该方法是ES6新增的方法,与全等类似,但是会考虑到一些边界情形。 使用全等:

console.log(-0 === +0) // true
console.log(NaN === NaN) // false

Object.is():

console.log(Object.is(+0,-0)) // false
console.log(Object.is(NaN,NaN)) // true