0%

对象属性配置

前言:

  • 我们知道js对象中可以存储属性和方法,对于存储的属性不只是”键值对”,对象属性实际上还有更加灵活强大的东西
  • 下面我们来看一下对象属性到底还有声明nb功能呢?

属性的分类

  • 对象属性有两种类别
  1. 数据属性:键值对形式
  2. 访问器属性:用于获取和设置值的函数

属性标志和属性描述符

  • 对象属性,除了**value** 外,还有三个特殊的特性(attributes)

    • writable — 如果为 true,则值可以被修改,否则它是只可读的。
    • enumerable — 如果为 true,则会被在循环中列出,否则不会被列出。
    • configurable — 如果为 true,则此特性可以被删除,这些属性也可以被修改,否则不可以。
  • 我们使用“常用的方式”创建的属性都是true

如何获取这些标识/特性?

//let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName);
//obj : 需要获取信息的对象
//propertyName: 属性的名称
//返回值: 所谓的“属性描述符对象“ 包含值和所有标志

let user = {
  name: "John"
};

let descriptor = Object.getOwnPropertyDescriptor(user, 'name');

alert( JSON.stringify(descriptor, null, 2 ) );
/* 属性描述符:
{
  "value": "John",
  "writable": true,
  "enumerable": true,
  "configurable": true
}
*/

如何修改这些标志呢?

// Object.defineProperty(obj, propertyName, descriptor)
// obj, propertyName: 对象及其属性
// descriptor: 应用的属性描述符"对象"

let user = {};

Object.defineProperty(user, "name", {
  value: "John"		//没有对应属性就新建一个对应属性
  //其他属性不写默认 false
});

let descriptor = Object.getOwnPropertyDescriptor(user, 'name');

alert( JSON.stringify(descriptor, null, 2 ) );
/*
{
  "value": "John",
  "writable": false,
  "enumerable": false,
  "configurable": false
}
 */

属性描述符的功能

1.只读writable
  • writable 谁行标志设置为false ,就将对应属性设置为只读属性
let user = {
    name: "minyue"
}
Object.defineProperty(user, "name", {
    writable: false
});
alert(user.name);	//minyue
user.name = "hcm";	//在严格模式下就会出错,非严格模式忽略
  • 对于一个新属性,我们需要明确列出那些true,未列出的默认false
  • 对于一个常规定义的属性,默认都是true,改什么就变什么
2.不可枚举enumerable
  • 对象的枚举,我们通过for in语句,是否能枚举对应的

    通常情况下,对象内置的toString 是不可枚举的,当我们在对象中,创建的属性或者方法,也不想被枚举出就可以设置enumerable:false

  • 同样不可枚举的属性也会被 object.keys(obj) (返回键的数组)

let user = {
    name: "minyue",
    toString() {
        return this.name;
    }
};
Object.defineProperty(user, "toString", {
    enumerable: false
});
for (let key in user) alert(key);	//name
alert(Object.keys(user));			//name
3.不可配置
  • 不可配置configurable:false 有时会预设在内建对象和属性中
  • 不可配置的属性思想:防止更改属性标志或删除属性标志
  • 特殊点:不可配置的可写属性是可以修改属性的值的!!!
//例如: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

批量获取/修改属性标志符

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

//克隆对象:首选方法,不只是值,还有属性描述符等
let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));

扩展:学到这里,我们知道,属性描述符都是单一属性级别上的工作,要想限制访问整个对象,参考

属性的getter和setter

getter和setter

  • 看完数据属性,下面我们来看一下访问器属性
  • 访问器属性:本质上是用于获取和设置值的函数

访问器属性由 “getter” 和 “setter” 方法表示。在对象字面量中,它们用 getset 表示:

let user = {
  name: "John",
  surname: "Smith",

  get fullName() {
      // 当读取 obj.propName 时,getter 起作用
      return `${this.name} ${this.surname}`;
  },

  set fullName(value) {
      // 当执行 obj.propName = value 操作时,setter 起作用
      [this.name, this.surname] = value.split(" ");
  }
};

user.fullName = "Alice Cooper";		// 调用setter
alert(user.fullname); 				// Alice Cooper
alert(user.surname); 				// Cooper

访问器描述符

  • get —— 一个没有参数的函数,在读取属性时工作,
  • set —— 带有一个参数的函数,当属性被设置时调用,
  • enumerable —— 与数据属性的相同,
  • configurable —— 与数据属性的相同。

使用 defineProperty 创建一个 fullName 访问器

let user = {
  name: "John",
  surname: "Smith"
};

Object.defineProperty(user, 'fullName', {
  get() {
    return `${this.name} ${this.surname}`;
  },

  set(value) {
    [this.name, this.surname] = value.split(" ");
  }
});

alert(user.fullName); // John Smith

for(let key in user) alert(key); // name, surname

巧妙的getter和setter

  • Getter/setter 可以用作“真实”属性值的包装器,以便对它们进行更多的控制。
//例如:当我们需要输入用户名不能太短时
let user = {
  get name() {
    return this._name;
  },

  set name(value) {
    if (value.length < 4) {
      alert("Name is too short, need at least 4 characters");
      return;
    }
    this._name = value;
  }
};

user.name = "Pete";
alert(user.name); // Pete

user.name = ""; // Name 太短了……
  • 访问器属性本身就是两个函数,没有值,存值一般会有一个内部数据属性存值!!(例如这里的_name)

访问器属性实例

  • 访问器的一大用途是,它们允许随时通过使用 getter 和 setter 替换“正常的”数据属性,来控制和调整这些属性的行为。
// 对于年龄这样的变化数据,我们希望存储birthday而不是age
// 这里我们就可以使用访问器属性,动态获取修改对象的age属性

function User(name, birthday) {
  this.name = name;
  this.birthday = birthday;

  // 年龄是根据当前日期和生日计算得出的
  Object.defineProperty(this, "age", {
    get() {
      let todayYear = new Date().getFullYear();
      return todayYear - this.birthday.getFullYear();
    }
  });
}

let john = new User("John", new Date(1999, 10, 29));

alert( john.birthday ); // Mon Nov 29 1999 00:00:00 GMT+0800 (中国标准时间)
alert( john.age );      // 21