Skip to main content
 首页 » 编程设计

Javascript面向对象之属性标志与描述

2022年07月19日147zhujiabin

Javascript面向对象(一)——属性标志与描述

我们都知道,对象可以存储属性。属性一般都是简单的键值对,但一个对象属性可以更复杂、优美。

属性标志

  • writable – 如果为 true, 属性可以改变,否则为只读。
  • enumerable – 如果为 true, 属性可以在loop…in中被列出,否则不被列出。
  • configurable – 如果为 true, 属性可以被删除或修改,否则不行。

这些属性通常很少使用,使用常规方式创建对象,他们的值都为true,但可以随时改变他们。
首先,看看如何获取他们的值。方法Object.getOwnPropertyDescriptor允许我们查询属性的完整信息。

let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName); 

其返回值一般称为“属性描述对象”,其包含属性值和所有标志。示例如下:

let user = { 
  name: "John" 
}; 
 
let descriptor = Object.getOwnPropertyDescriptor(user, 'name'); 
 
alert( JSON.stringify(descriptor, null, 2 ) ); 
/* property descriptor: 
{ 
  "value": "John", 
  "writable": true, 
  "enumerable": true, 
  "configurable": true 
} 
*/ 

改变标志,可以使用Object.defineProperty,语法如下:

Object.defineProperty(obj, propertyName, descriptor) 

如果属性已经存在,则更新标识,否则使用给定的值和属性标识创建。注意:如果没有提供标识,默认为false
示例,这里属性name被创建,所有标识为false

let user = {}; 
 
Object.defineProperty(user, "name", { 
  value: "John" 
}); 
 
let descriptor = Object.getOwnPropertyDescriptor(user, 'name'); 
 
alert( JSON.stringify(descriptor, null, 2 ) ); 
/* 
{ 
  "value": "John", 
  "writable": false, 
  "enumerable": false, 
  "configurable": false 
} 
*/ 

和正常方式创建对象相比,所有标识为false,如果没有特殊需要,我们最好设置其为true。下面详述每个属性标识。

 只读

我们使user.name只读,通过改变writable标识:

let user = { 
  name: "John" 
}; 
 
Object.defineProperty(user, "name", { 
  writable: false 
}); 
 
user.name = "Pete"; // Error: Cannot assign to read only property 'name'... 

现在我们不能修改user对象的name属性值,除非我们使用defineProperty方法覆盖标识。
下面示例效果一样,但对象的属性事前不存在。

let user = { }; 
 
Object.defineProperty(user, "name", { 
  value: "Pete", 
  // for new properties need to explicitly list what's true 
  enumerable: true, 
  configurable: true 
}); 
 
alert(user.name); // Pete 
user.name = "Alice"; // Error 

 非枚举

现在我们user对象增加自定义toString方法。
通常,对象内置的toString方法是非枚举的,使用for..in不会显示,但是如果增加toString方法,则可以通过for..in列出。如果我们不希望被列出,可以使用enumerable:false.则不会被列出,和内置的一样。

let user = { 
  name: "John", 
  toString() { 
    return this.name; 
  } 
}; 
 
// By default, both our properties are listed: 
for(let key in user) alert(key); // name, toString 
 
Object.defineProperty(user, "toString", { 
  enumerable: false 
}); 
 
// Now toString disappears: 
for(let key in user) alert(key); // name 

非枚举属性通过Object.keys方法通用也不能被枚举。

非配置

非配置标识(configurable:false)通常设置在内置对象属性中使用。
非配置标识不能通过defineProperty方法修改或删除。
示例:Math.PI同时是只读、非枚举、非配置:

let descriptor = Object.getOwnPropertyDescriptor(Math, 'PI'); 
 
alert( JSON.stringify(descriptor, null, 2 ) ); 
/* 
{ 
  "value": 3.141592653589793, 
  "writable": false, 
  "enumerable": false, 
  "configurable": false 
} 
*/ 

所以,开发者不能改变或覆盖他们。

Math.PI = 3; // Error 
 
// delete Math.PI won't work either 

使属性为非配置,是单向的,不能改回来。因为defineProperty方法在非配置属性上不工作。
这里我们使user.name用于密封。

let user = { }; 
 
Object.defineProperty(user, "name", { 
  value: "John", 
  writable: false, 
  configurable: false 
}); 
 
// won't be able to change user.name or its flags 
// all this won't work: 
//   user.name = "Pete" 
//   delete user.name 
//   defineProperty(user, "name", ...) 
Object.defineProperty(user, "name", {writable: true}); // Error 

使用严格模式出现错误
在非严格模式下,修改只读属性不会报错。但属性不会改变。强制修改标志在非严格模式下被安静地忽略。

 Object.defineProperties

方法Object.defineProperties(obj,descriptors)允许一次定义多个属性。语法如下:

Object.defineProperties(obj, { 
  prop1: descriptor1, 
  prop2: descriptor2 
  // ... 
}); 

示例:

Object.defineProperties(user, { 
  name: { value: "John", writable: false }, 
  surname: { value: "Smith", writable: false }, 
  // ... 
}); 

Object.getOwnPropertyDescriptors

使用方法Object.getOwnPropertyDescriptors(obj)方法可以一次性获取多个描述。
Object.defineProperties一起使用,他能够使用“标识感知”的方式克隆对象:

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

通常我们克隆对象,使用拷贝属性赋值,代码如下:
for(let key in user) {
clone[key] = user[key]
}
但是,这样并没有拷贝属性,因此要完全克隆,在克隆之前使用Object.defineProperties方法。

全局密封对象

属性描述在在单个属性级别上起作用。也有一些方法实现限制整个对象。

Object.preventExtensions(obj)
禁止给对象增加属性

Object.seal(obj)
禁止增加/删除属性,设置所有存在属性的configurable为false。

Object.freeze(obj)
禁止增加/删除属性,设置所有存在属性的configurable:false, writable: false.

一些测试方法如下:

Object.isExtensible(obj)
Returns false if adding properties is forbidden, otherwise true.

Object.isSealed(obj)
Returns true if adding/removing properties is forbidden, and all existing properties have configurable: false.

Object.isFrozen(obj)
Returns true if adding/removing/changing properties is forbidden, and all current properties are configurable: false, writable: false.

这写方法实际情况很少使用。


本文参考链接:https://blog.csdn.net/neweastsun/article/details/69055998