秋雨
首页
  • HTML
  • CSS
  • JavaScript
设计模式
webpack
  • 前端常识
  • ES
  • React
  • Redux
  • ReactRouter
  • 事件循环
  • 浏览器渲染流程
  • Vue项目性能优化
  • Vue
  • App
Github
首页
  • HTML
  • CSS
  • JavaScript
设计模式
webpack
  • 前端常识
  • ES
  • React
  • Redux
  • ReactRouter
  • 事件循环
  • 浏览器渲染流程
  • Vue项目性能优化
  • Vue
  • App
Github
  • JavaScript基础
  • Object(基础)
  • 数据类型
  • 函数进阶
  • 对象属性配置
  • 原型、继承
  • class
  • 错误处理
  • Promise

对象属性配置

属性标志和属性描述符

属性标志

对象属性(properties),除value外还有三个特殊的特性(attributes),也就是所谓的‘标志’。

  • writable- 如果为true,则值可以被修改,否则它是只可读的。
  • enumerable- 如果为true,则会呗在循环中列出,否则不会被列出。
  • configurable- 如果为true,则此属性可以被删除。

获取这些标志使用:

Object.getOwnPropertyDescriptor(obj, propertyName);
  • obj:需要查找的对象
  • propertyName:需要查找的属性
const obj = {
  a: 1,
};

Object.getOwnPropertyDescriptor(obj, "a");

// {
//   value: 1,
//   writable: true,
//   enumerable: true,
//   configurable: true
// }

修改属性标志

Object.defineProperty(obj, propertyName, descriptor);
  • obj,propertyName:需要修改的对象和属性
  • descriptor:需要修改的属性标志

如果没有提供任何标志,则会假定它是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
}
 */

只读

通过修改writable标志来把user.name设置为只读。

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

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

user.name = "Pete"; // Error: Cannot assign to read only property 'name'

只在严格模式下才会出现 Errors

非严格模式下,在对不可写的属性等进行写入操作时,不回出现错误。但是操作不会成功。

let user = {};

Object.defineProperty(user, "name", {
  value: "John",
  // 对于新属性,我们需要明确地列出哪些是 true
  enumerable: true,
  configurable: true,
});

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

不可枚举

let user = {
  name: "John",
  toString() {
    return this.name;
  },
};

// 默认情况下,我们的两个属性都会被列出:
for (let key in user) alert(key); // name, toString

将enumerable:false。这样就不会出现在for...in循环中了。

let user = {
  name: "John",
  toString() {
    return this.name;
  },
};

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

// 现在我们的 toString 消失了:
for (let key in user) alert(key); // name

不可枚举的属性也会被Object.keys排除。

alert(Object.keys(user)); // name

不可配置

configurable:false

let descriptor = Object.getOwnPropertyDescriptor(Math, "PI");

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

因此开发人员无法修改Math.PI的值或者覆盖它。

Math.PI = 3; // Error,因为其 writable: false

// 删除 Math.PI 也不会起作用
// 也无法对其进行更改
Object.defineProperty(Math, "PI", { writable: true });

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

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

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

user.name = "Pete"; // 正常工作
delete user.name; // Error

将user.name设置为一个永不可改的常量,就想内建的Math.PI

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

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

// 不能修改user.name 或它的标志
// 下面的操作都不起作用

user.name = "Pete";
delete user.name;
Object.defineProperty(user, "name", { value: "Pete" });

唯一可行的特性更改:writable true -> false

对于不可配置的属性,我们可以将writable:true改为false,从而防止其值被修改(以添加另一层保护)。但无法反向操作。

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

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

// 不能修改 user.name 或它的标志
// 下面的所有操作都不起作用:

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

Object.defineProperties

一次设置多条属性

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

Object.getOwnPropertyDescriptors

一次获取所有属性描述符。

Object.getOwnPropertyDescriptors(obj);
let user = {
  name: "John",
    age:18
};

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

let clone = Object.getOwnPropertyDescriptors(user);
console.log(clone);

// {
//     "name": {
//         "value": "John",
//         "writable": false,
//         "enumerable": true,
//         "configurable": false
//     },
//     "age": {
//         "value": 18,
//         "writable": true,
//         "enumerable": true,
//         "configurable": true
//     }
// }

我们可以将它用在克隆上,for...in循环和Object.assign只会复制数据,而不会复制描述符。另外for...in循环会忽略symbol类型和不可枚举的属性。

let user = {
  name: "John",
    age:18
};

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

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

其他属性

  • Object.preventExtensions(obj)

禁止向对象添加新属性

  • Object.seal(obj)

禁止添加/删除属性。为所有现有的属性设置configurable:false。

  • Object.freeze(obj)

禁止添加/删除/更爱属性。为所有现有的属性设置writable:false和configurable:false。

  • Object.isExtensible(obj)

如果添加属性被禁止,则返回false,否则返回true。

  • Object.isSealed(obj)

如果添加/删除属性被禁止,并且所有现有的属性都具有 configurable: false则返回 true。

  • Object.isFrozen(obj)

如果添加/删除/更改属性被禁止,并且所有当前属性都是 configurable: false, writable: false,则返回 true。

属性的getter和setter

有两种类型的对象属性

  • 数据属性
  • 访问器属性(accessor property)。它们本质是用来获取和设置值的函数,但从外部代码来看就像是常规属性。

getter和setter

let obj = {
  get propName(){
    // 读取操作
  },

  set propName(value)[
    // 写入操作
  ]
}
let user = {
  name: "John",
  surname: "Smith",

  get fullName() {
    return `${this.name} ${this.surname}`;
  }

  set fullName(value){
    [this.name, this.surname] = value.split(" ");
  }
};
console.log(user.fullName); // John Smith

// set fullName 将以给定值执行
user.fullName = "Alice Cooper";

console.log(user.fullName);// Alice Cooper

访问器描述符

与数据属性的不同

对于访问器属性,没有value和writable,但是有get和set函数。

所以访问器描述符可能有

  • get -- 一个没有参数的函数,在读取属性时工作。
  • set -- 带有一个参数的函数,当属性被设置时调用。
  • enumerable -- 与数据属性的相同
  • configurable -- 与数据属性的相同
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

对属性进行控制

let user = {
  get name(){
    return this._name;
  },

  set name(value){
    if(value.length<4){
      console.log('赋值太短了')
      return
    }

    this._name = value;
  }
}

user.name = "Pete";
console.log(user.name); // Pete
user.name = '';//赋值太短了

兼容性

function User(name, age) {
  this.name = name;
  this.age = age;
}

let john = new User("John", 25);

alert( john.age ); // 25
// 有天我们突然想存储一个精确的日期但是,这时候 又会影响之前的代码
function User(name, birthday) {
  this.name = name;
  this.birthday = birthday;
}

let john = new User("John", new Date(1992, 6, 1));
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(1992, 6, 1));

alert( john.birthday ); // birthday 是可访问的
alert( john.age );      // ……age 也是可访问的

这样我们就可以既保留age又可以保留birthday了

最后更新:
贡献者: qiuyulc
Prev
函数进阶
Next
原型、继承