JavaScript对象(Object)研究_03_freeze、fromEntries、getOwnPropertyDescriptor、getOwnPropertyDescriptors

JavaScript对象(Object)研究_03_freeze、fromEntries、getOwnPropertyDescriptor、getOwnPropertyDescriptors

在JavaScript中,Object构造函数提供了一系列静态方法,用于操作和管理对象的属性和行为。本篇博客将深入探讨Object的四个重要静态方法:freezefromEntriesgetOwnPropertyDescriptorgetOwnPropertyDescriptors。通过详细的介绍、语法说明、代码示例,以及扩展的知识点,我们将全面了解这些方法的用途、工作原理和注意事项。

一、Object.freeze()

1. 基础介绍

Object.freeze()方法可以冻结一个对象。冻结对象后,不能向其添加新属性,不能删除已有属性,不能修改已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。换句话说,这个对象变得不可变(immutable)。

冻结是浅层次的,也就是说,如果对象的属性是引用类型(如对象、数组),那么这些引用类型的属性仍然可以修改其内部的值。

2. 语法

Object.freeze(obj)
  • obj:要被冻结的对象。

3. 示例代码

示例1:冻结简单对象
const obj = {
  prop1: 'value1',
  prop2: 'value2'
};

Object.freeze(obj);

obj.prop1 = 'newValue'; // 无效操作,属性值不会改变
console.log(obj.prop1); // 输出: 'value1'

obj.prop3 = 'value3'; // 无效操作,无法添加新属性
console.log(obj.prop3); // 输出: undefined

delete obj.prop2; // 无效操作,无法删除属性
console.log(obj.prop2); // 输出: 'value2'
示例2:冻结嵌套对象
const person = {
  name: 'Alice',
  hobbies: ['reading', 'traveling']
};

Object.freeze(person);

person.name = 'Bob'; // 无效操作
console.log(person.name); // 输出: 'Alice'

person.hobbies.push('coding'); // 有效操作,修改了引用类型内部的值
console.log(person.hobbies); // 输出: ['reading', 'traveling', 'coding']

4. 检测对象是否被冻结

使用Object.isFrozen()方法可以检测一个对象是否被冻结。

console.log(Object.isFrozen(person)); // 输出: true

5. 深冻结对象

由于Object.freeze()是浅冻结,如果需要深度冻结对象,需要递归地冻结每个属性。

function deepFreeze(obj) {
  // 取出所有属性名
  const propNames = Object.getOwnPropertyNames(obj);

  // 递归地冻结属性
  for (const name of propNames) {
    const value = obj[name];

    if (typeof value === 'object' && value !== null) {
      deepFreeze(value);
    }
  }

  return Object.freeze(obj);
}

deepFreeze(person);

person.hobbies.push('painting'); // 无效操作
console.log(person.hobbies); // 输出: ['reading', 'traveling', 'coding']

6. 注意事项

  • 不可逆:一旦对象被冻结,就无法解冻。

  • 严格模式下的错误:在严格模式下,试图修改被冻结对象的属性会抛出TypeError错误。在非严格模式下,修改操作会被静默忽略。

  • 冻结数组:冻结数组后,无法修改其元素或长度。

    const arr = [1, 2, 3];
    Object.freeze(arr);
    
    arr[0] = 10; // 无效操作
    arr.push(4); // 无效操作
    console.log(arr); // 输出: [1, 2, 3]
    

7. 扩展知识点

  • 不可变对象的应用:在函数式编程中,不可变对象有助于避免副作用,提高代码的可预测性和可维护性。

  • const的区别const声明的变量是常量,指向的对象引用不可变,但对象本身是可变的。Object.freeze()冻结的是对象本身,使其不可变。

    const obj = { a: 1 };
    obj.a = 2; // 有效操作
    
    Object.freeze(obj);
    obj.a = 3; // 无效操作
    

二、Object.fromEntries()

1. 基础介绍

Object.fromEntries()方法将一个键值对数组(或具有可迭代[key, value]对的对象)转换为一个对象。它是Object.entries()的逆操作。

2. 语法

Object.fromEntries(iterable)
  • iterable:一个可迭代的键值对([key, value])的对象,如数组或Map

3. 示例代码

示例1:从键值对数组创建对象
const entries = [
  ['name', 'Alice'],
  ['age', 25],
  ['city', 'New York']
];

const obj = Object.fromEntries(entries);
console.log(obj);
// 输出: { name: 'Alice', age: 25, city: 'New York' }
示例2:从Map创建对象
const map = new Map([
  ['color', 'blue'],
  ['size', 'medium']
]);

const obj = Object.fromEntries(map);
console.log(obj);
// 输出: { color: 'blue', size: 'medium' }
示例3:处理查询参数
const queryString = 'name=Alice&age=25';

const params = new URLSearchParams(queryString);

const obj = Object.fromEntries(params);
console.log(obj);
// 输出: { name: 'Alice', age: '25' }

4. 注意事项

  • 键的覆盖:如果键重复,后面的值会覆盖前面的值。

    const entries = [
      ['a', 1],
      ['b', 2],
      ['a', 3]
    ];
    
    const obj = Object.fromEntries(entries);
    console.log(obj); // 输出: { a: 3, b: 2 }
    
  • 非字符串键:键会被转换为字符串类型。

    const entries = [
      [1, 'one'],
      [true, 'bool']
    ];
    
    const obj = Object.fromEntries(entries);
    console.log(obj); // 输出: { '1': 'one', 'true': 'bool' }
    

5. 扩展知识点

  • Object.entries()结合使用

    const obj = { a: 1, b: 2, c: 3 };
    const newObj = Object.fromEntries(
      Object.entries(obj).map(([key, value]) => [key, value * 2])
    );
    
    console.log(newObj); // 输出: { a: 2, b: 4, c: 6 }
    
  • 过滤对象属性

    const obj = { a: 1, b: 2, c: 3 };
    
    const filteredObj = Object.fromEntries(
      Object.entries(obj).filter(([key, value]) => value > 1)
    );
    
    console.log(filteredObj); // 输出: { b: 2, c: 3 }
    

三、Object.getOwnPropertyDescriptor()

1. 基础介绍

Object.getOwnPropertyDescriptor()方法返回指定对象上一个自有属性对应的属性描述符。如果没有对应的属性,返回undefined

2. 语法

Object.getOwnPropertyDescriptor(obj, prop)
  • obj:要查找的对象。
  • prop:要获取其属性描述符的属性名。

3. 示例代码

示例1:获取数据属性描述符
const obj = { a: 1 };

const descriptor = Object.getOwnPropertyDescriptor(obj, 'a');
console.log(descriptor);
// 输出: { value: 1, writable: true, enumerable: true, configurable: true }
示例2:获取存取属性描述符
const obj = {
  get b() {
    return 2;
  }
};

const descriptor = Object.getOwnPropertyDescriptor(obj, 'b');
console.log(descriptor);
// 输出: { get: [Function: get b], set: undefined, enumerable: true, configurable: true }
示例3:属性不存在
const descriptor = Object.getOwnPropertyDescriptor(obj, 'c');
console.log(descriptor); // 输出: undefined

4. 属性描述符的属性

1. 数据描述符(Data Descriptor)
  • value:属性的值。
  • writable:属性的值是否可写。
  • enumerable:属性是否可枚举。
  • configurable:属性的描述符是否可修改,属性是否可删除。
2. 存取描述符(Accessor Descriptor)
  • get:获取属性值的函数。
  • set:设置属性值的函数。
  • enumerable:同上。
  • configurable:同上。

5. 注意事项

  • 仅获取自有属性:该方法只会返回对象自身的属性描述符,不会查找原型链上的属性。
  • 区分属性类型:通过查看描述符,可以区分属性是数据属性还是存取属性。

6. 扩展知识点

  • 应用于调试和测试

    了解属性的描述符,有助于调试代码,尤其是在涉及属性的可写性、可枚举性和可配置性时。

  • Object.defineProperty()结合使用

    可以先获取一个属性的描述符,修改后再定义到另一个对象上。

    const obj1 = { a: 1 };
    const descriptor = Object.getOwnPropertyDescriptor(obj1, 'a');
    
    const obj2 = {};
    Object.defineProperty(obj2, 'b', descriptor);
    
    console.log(obj2.b); // 输出: 1
    

四、Object.getOwnPropertyDescriptors()

1. 基础介绍

Object.getOwnPropertyDescriptors()方法返回一个对象的所有自身属性的属性描述符。该方法的返回值是一个对象,其属性名是原对象的属性名,属性值是对应的属性描述符。

2. 语法

Object.getOwnPropertyDescriptors(obj)
  • obj:要获取属性描述符的对象。

3. 示例代码

示例1:获取对象的所有属性描述符
const obj = {
  a: 1,
  get b() {
    return 2;
  }
};

const descriptors = Object.getOwnPropertyDescriptors(obj);
console.log(descriptors);

/* 输出:
{
  a: { value: 1, writable: true, enumerable: true, configurable: true },
  b: {
    get: [Function: get b],
    set: undefined,
    enumerable: true,
    configurable: true
  }
}
*/
示例2:实现对象的完整克隆
const obj = {
  a: 1,
  get b() {
    return this.a * 2;
  }
};

const clone = Object.create(
  Object.getPrototypeOf(obj),
  Object.getOwnPropertyDescriptors(obj)
);

console.log(clone.b); // 输出: 2

clone.a = 3;
console.log(clone.b); // 输出: 6

4. 注意事项

  • 自有属性:仅获取对象自身的属性描述符,不包括原型链上的属性。
  • 可用于复制对象的属性:在复制对象时,使用Object.defineProperties()Object.getOwnPropertyDescriptors()可以复制属性的所有特性,包括访问器。

5. 扩展知识点

  • 复制对象时保留属性特性

    function completeClone(origin) {
      return Object.create(
        Object.getPrototypeOf(origin),
        Object.getOwnPropertyDescriptors(origin)
      );
    }
    
    const obj = {
      a: 1,
      get b() {
        return this.a * 2;
      }
    };
    
    const clone = completeClone(obj);
    console.log(clone.b); // 输出: 2
    
  • 实现浅拷贝的另一种方法

    Object.assign()不同,使用Object.getOwnPropertyDescriptors()可以复制属性的特性。

五、总结

在本篇博客中,我们深入探讨了Object构造函数的四个重要静态方法:freezefromEntriesgetOwnPropertyDescriptorgetOwnPropertyDescriptors

  • Object.freeze():用于冻结对象,使其不可变。适用于需要创建常量对象的场景。
  • Object.fromEntries():将键值对数组或可迭代对象转换为普通对象,方便处理数据转换。
  • Object.getOwnPropertyDescriptor():获取对象某个自有属性的属性描述符,便于了解属性的特性和行为。
  • Object.getOwnPropertyDescriptors():获取对象所有自有属性的属性描述符,适用于对象的深度复制和属性特性的复制。

在实际开发中,充分理解和合理应用这些方法,可以帮助我们更有效地操作对象,提高代码的健壮性和可维护性。

注意事项总结

  • 冻结对象时的限制Object.freeze()是浅冻结,嵌套对象需要深冻结。
  • fromEntries()键的处理:键会被转换为字符串类型,重复键会被后面的值覆盖。
  • 属性描述符的重要性:了解属性的描述符有助于精确控制对象的属性行为,避免意外的修改或访问。
  • 深度复制对象:使用Object.getOwnPropertyDescriptors()Object.create()可以实现对象的完整克隆,包括属性特性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

It'sMyGo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值