目录

💡 JavaScript 对象属性标志和属性描述符

# 属性标志

对象的属性(properties)除了 value 值以外,还有三个特殊的 特性(attributes),也是 标志

  • writable 可写:为 true ,表示该值可以修改。否则为只可读的。
  • enumerable 可迭代:如果为 true ,则会被在循环中列出。否则不会被列出。
  • configurable :如果为 true ,则此属性可以被删除,这些特性也可以被修改,否则不可以。

在使用 常用的方式 创建一个属性时,这些标志 默认都为 true

  • 要获取这些标志的信息,可以使用 Object.getOwnPropertyDescriptor 方式查询有关属性的完整信息,语法为:

    let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName)
    
    1
    • obj :要获取信息的对象。
    • propertyName :属性名称。

    该方法的返回值是一个 「属性描述符」 对象, 包含 值 和所有的标志。

    🌰 例子:

    let user = {
      name: "John"
    };
    
    let descriptor = Object.getOwnPropertyDescriptor(user, 'name');
    console.log(JSON.stringify(descriptor, null, 2))
    /*
    	"value": "John",
      "writable": true,
      "enumerable": true,
      "configurable": true
    */
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
  • 修改 这些标志,可以使用 Object.defineProperty ,语法为:

    Object.defineProperty(obj, propertyName, descriptor)
    
    1
    • objpropertyName :同上。
    • descriptor :要应用的 属性描述符对象

    如果该属性存在, defineProperty 会更新其标志,否则会使用 特定的值 和标志创建属性,这种情况下没有提供标志则 假定 它为 false

    🌰 例子:

    let user = {}
    
    Object.defineProperty(user, "name", {
      value: "John"
    })
    
    let descriptor = Object.getOwnPropertyDescriptor(user, name)
    console.log(JSON.stringify(descriptor, null, 2))
    /*
    	value": "John",
      "writable": false,
      "enumerable": false,
      "configurable": false
    */
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    这种创建属性的方式与上面常用的普通创建属性的方法进行比较,现在所有的 标志 都被假定为 false ,可以在创建属性时通过 descriptor 中设置为 true

# 只读属性

可以通过将 标志 writable 设置为 false ,使对象中的属性为只读(即不能重新赋值)。

🌰 例子:

let user = {
  name: 'John'
}

Object.defineProperty(user, 'name', {
  writable: false
})

user.name = 'Pete' // Error
1
2
3
4
5
6
7
8
9

🌰 例子:

let user = { };

Object.defineProperty(user, "name", {
  value: "John",
	// 对于新创建的属性需要列出要为 true 的标志,否则为 false
  enumerable: true,
  configurable: true
});

console.log(user.name); // John
user.name = "Pete"; // Error
1
2
3
4
5
6
7
8
9
10
11

# 不可枚举属性

🌰 例子:一般属性自带的 toString() 方法默认是不可枚举的,即在 for ... in 中不会出现。但是如果自定义重写的 toString() 时,它会出现在 for ... in 中。

let user = {
  name: 'Simon',
  toString() {
    return this.name
  }
}

for(let key in user) console.log(key) // name, toString
1
2
3
4
5
6
7
8

toString 设置为不可枚举:

Object.defineProperty(user, "toString", {
  enumerable: false
})

for(let key in user) console.log(key) // name

console.log(Object.keys(user)) // name
1
2
3
4
5
6
7

# 不可配置属性

不可配置的属性不能被删除,它的 特性(attribute) 不能被修改。

🌰 例子 / Math.PI 是可读的、不可枚举、不可配置的:

let descriptor = Object.getOwnPropertyDescriptor(Math, 'PI');
console.log(JSON.stringify(descriptor, null, 2 ))
/*
{
  "value": 3.141592653589793,
  "writable": false,
  "enumerable": false,
  "configurable": false
}
*/
1
2
3
4
5
6
7
8
9
10

所以,并不能修改 Math.PI 的值,或者覆盖它。也无法修改它的特性, writable 或者 enumerable

注意

可以从例子中看出,只要将 configurable 设置为 false ,之后再也无法通过 defineProperty 更改回来。

注意:configurable: false 防止更改和删除属性标志,但是允许更改对象的值。

🌰 例子:

let user = {
  name: "John"
};

Object.defineProperty(user, "name", {
  configurable: false
});

user.name = "Pete" // 正常工作
delete user.name // 出错
1
2
3
4
5
6
7
8
9
10

name 属性同时设置为 writable: false ,此时该属性就会像内建的 Math.PI 一样,永不可改:

Object.defineProperty(user, "name", {
  writable: false,
  configurable: false
});

user.name = "Pete" // 出错
delete user.name // 出错
Object.defineProperty(user, "name", { value: "Pete" }); // 出错
1
2
3
4
5
6
7
8

提示

唯一可以修改的特性( writable ):对于不可配置的属性,可以将 writbale 设置为 false ,以防止其值被修改(以添加另一层保护)。但无法反向行之。

# Object.defineProperties(obj, descriptors)

该方法允许一次定义多个属性,语法为:

Object.defineProperties(obj, {
  prop1: descriptor1,
  prop2: descriptor2
  // ...
});
1
2
3
4
5

🌰 例子:

Object.defineProperties(user, {
  name: { value: "John", writable: false },
  surname: { value: "Smith", writable: false },
  // ...
})
1
2
3
4
5

# Object.getOwnPropertyDescriptors(obj)

该方法可以一次获取 所有属性描述符

通常在克隆一个对象时,使用 for key in obj 遍历赋值的方式进行克隆,但是不能复制 包括 symbol 类型的和不可枚举属性在内的所有 属性描述符。

深层完全克隆方式:可以与 Object.defineProperties 一起用作 克隆对象 的 「标志感知」 方式:

let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));
1

因为 Object.getOwnPropertyDescriptors(obj) 可以返回包含 symbol 类型的和不可枚举的属性在内的 所有属性描述符

📢 上次更新: 2022/09/02, 10:18:16