JavaScript之对象的属性

一.属性描述符

了解什么是属性描述符,我们从定义一个最简单的对象开始,如下对象obj,之后添加一个属性a,然后使用getOwnPropertyDescriptor()方法来获取属性描述符:

var obj = {};
obj.a = 1;
console.log(Object.getOwnPropertyDescriptor( obj, "a"));
//{ value: 1, writable: true, enumerable: true, configurable: true }

根据结果可以看出一共有4个属性描述符:

  • value:显而易见,就是值,没什么可说的,代表的就是obj.a的值
  • writable:是否可写,其实就是表示是否是只读属性,如果为true,则可写,true为默认值,如果为false,则定义后值不可被改变:
var obj = {};
Object.defineProperty(obj,"a",{
  value: 1,
  writable: false,
  enumerable: true,
  configurable: true
});
obj.a = 2;
console.log(obj.a); //1
  • enumerable:表示该属性是否可枚举,默认为true表示可枚举,其实这个属性的作用就是在用for in循环遍历此对象的时候是否包含此属性:
var obj = {a: 1};
Object.defineProperty(obj, "b", {
  value: 2,
  writable: true,
  enumerable: false,
  configurable: true
});
for (var i in obj) {
  console.log(i); //a  只有a而没有b
}
  • configurable:表示该属性是否可配置,如禁止属性被删除:
var obj = {};
obj.a = 1;
delete obj.a;
console.log(obj.a);//undefined

Object.defineProperty(obj, "b", {
  value: 2,
  writable: true,
  enumerable: true,
  configurable: false
});
delete obj.b;
console.log(obj.b);//2

注意:configurable从true改为false的操作是单向的,无法撤销,也就是说configurable从true改为false之后是不能再改回到true的,如执行下面的代码会报错:

var obj = {};
Object.defineProperty(obj, "a", {
  value: 2,
  writable: true,
  enumerable: true,
  configurable: false
});
Object.defineProperty(obj, "a", {
  value: 2,
  writable: true,
  enumerable: true,
  configurable: true
});

这里还有一个需要注意的地方,将configurable改为false之后,writable仍然可以从true改为false,但是无法由false改为true,如下:

var obj = {};
Object.defineProperty(obj, "a", {
  value: 2,
  writable: true,
  enumerable: true,
  configurable: false
});
Object.defineProperty(obj, "a", {
  value: 2,
  writable: false,
  enumerable: true,
  configurable: false
});
console.log(Object.getOwnPropertyDescriptor(obj, "a"));
// { value: 2,
//   writable: false,
//   enumerable: true,
//   configurable: false }

二.属性不变性

  • 禁止扩展:Object.preventExtensions(…)

    禁止一个对象添加新属性并且保留已有属性:

var obj = {a: 2};
Object.preventExtensions(obj);
obj.b = 3;
console.log(obj.b);// undefined
  • 密封:Object.seal(..)

    Object.seal(..) 会创建一个“密封”的对象,这个方法实际上会在一个现有对象上调用Object.preventExtensions(..) 并把所有现有属性标记为 configurable:false 。所以,密封之后不仅不能添加新属性,也不能重新配置或者删除任何现有属性(虽然可以修改属性的值)。

  • 冻结:Object.freeze(..)

    Object.freeze(..) 会创建一个冻结对象, 这个方法实际上会在一个现有对象上调用Object.seal(..) 并把所有“数据访问”属性标记为 writable:false ,这样就无法修改它们的值。这个方法是你可以应用在对象上的级别最高的不可变性,它会禁止对于对象本身及其任意直接属性的修改
    “深度冻结”一个对象:具体方法为,首先在这个对象上调用 Object.freeze(..) ,
    然后遍历它引用的所有对象并在这些对象上调用 Object.freeze(..)

三.getter和setter

当你给一个属性定义 getter、setter 或者两者都有时,这个属性会被定义为“访问描述符”(和“数据描述符”相对)。对于访问描述符来说,JavaScript 会忽略它们的 value 和writable 特性,取而代之的是关心 set 和 get (还有 configurable 和 enumerable )特性
如:

var obj = {
  get a() {
    return 1;
  }
};
Object.defineProperty(obj, 'b', {
    get: function () {
      return this.a * 2;
    },
    // 确保 b 会出现在对象的属性列表中
    enumerable: true
  }
);
console.log(Object.getOwnPropertyDescriptor(obj, "a"));
// { get: [Function: get a],
//   set: undefined,
//     enumerable: true,
//   configurable: true }
console.log(Object.getOwnPropertyDescriptor(obj, "b"));
// { get: [Function: get],
//   set: undefined,
//     enumerable: true,
//   configurable: false }

不管是对象文字语法中的 get a() { .. } ,还是 defineProperty(..) 中的显式定义,二者都会在对象中创建一个不包含值的属性,对于这个属性的访问会自动调用一个隐藏函数,它的返回值会被当作属性访问的返回值:

var obj = {
  get a() {
    return 1;
  }
};
obj.a = 2;
console.log(obj.a); //1

由于我们只定义了 a 的 getter,所以对 a 的值进行设置时 set 操作会忽略赋值操作,不会抛出错误。而且即便有合法的 setter,由于我们自定义的 getter 只会返回 1,所以 set 操作是没有意义的。
为了让属性更加合理,setter和getter通常成对出现(只定义一个的话通常会产生意料之外的行为),setter会覆盖单个属性默认的赋值操作:

var obj = {
// 给 a 定义一个 getter
  get a() {
    return this._a_;
  },
// 给 a 定义一个 setter
  set a(val) {
    this._a_ = val;
  }
};
obj.a = 2;
console.log(obj.a); //2
console.log(Object.getOwnPropertyDescriptor(obj, "a"));
// { get: [Function: get a],
//   set: [Function: set a],
//   enumerable: true,
//     configurable: true }

猜你喜欢

转载自blog.csdn.net/qq_23158083/article/details/73647688