likes
comments
collection
share

JavaScript之Object.defineProperty和Object.defineProperties详解

作者站长头像
站长
· 阅读数 31

一.Object.defineProperty() 

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

注意:应当直接在 Object 构造器对象上调用此方法,而不是在任意一个 Object 类型的实例上调用。如:

语法:

Object.defineProperty(obj, prop, descriptor)

obj:要定义属性的对象。

prop:要定义或修改的属性的名称或 Symbol 。

descriptor:要定义或修改的属性描述符。

给对象添加一个属性时,经常使用object.param的方式,或者直接在对象中挂载。

const person = { name: 'hj' }

在ECMAScript5中,对每个属性都添加了几个属性类型,来描述这些属性的特点。他们分别是

  • configurable:   默认false
  • enumerable:  默认false
  • writable:  默认false
  • value :  默认undefined

1.configurable 特性表示对象的属性是否可以被删除,以及除 value 和 writable 特性外的其他特性是否可以被修改。

当第一次设置为false后,再改写是不可以的。属性值也是不能被删除的。

var obj = {};
Object.defineProperty(obj , 'a', {
  get() { return 1; },
  configurable: false
});
// 定义为false 后  enumerable set get configurable value 都是不能再设置了。 delete o.a 也是删不了的
Object.defineProperty(obj , 'a', {
  configurable: true
}); // throws a TypeError  抛出错误
Object.defineProperty(obj , 'a', {
  enumerable: true
}); // 抛出错误
Object.defineProperty(obj , 'a', {
  set() {}
}); // 报错
Object.defineProperty(obj , 'a', {
  get() { return 1; }
}); // throws a TypeError
// (even though the new get does exactly the same thing)
Object.defineProperty(obj , 'a', {
  value: 12
}); // throws a TypeError // ('value' can be changed when 'configurable' is false but not in this case due to 'get' accessor)
 
console.log(obj .a); // logs 1
delete obj .a; // Nothing happens
console.log(obj .a); // logs 1  还存在说明没删掉

2.enumerable 定义了对象的属性是否可以在 for...in 循环和 Object.keys() 中被枚举。

for…in 循环和 Object.keys()  定义: 任意顺序遍历一个对象的除Symbol以外的可枚举属性。

var obj= {};
Object.defineProperty(obj, "a", { value : 1, enumerable: true });
Object.defineProperty(obj, "b", { value : 2, enumerable: false });
Object.defineProperty(obj, "c", { value : 3 }); // enumerable 默认为 false
obj.d = 4; // 如果使用直接赋值的方式创建对象的属性,则 enumerable 为 true
Object.defineProperty(obj, Symbol.for('e'), {
  value: 5,
  enumerable: true
});
Object.defineProperty(obj, Symbol.for('f'), {
  value: 6,
  enumerable: false
});
 
for (var i in obj) {
  console.log(i);
}
// 只会打印a 和 d 
 
Object.keys(obj); // ['a', 'd']

 打印 对象 obj 在谷歌浏览器中查看

  JavaScript之Object.defineProperty和Object.defineProperties详解

 很明显颜色深的是可枚举的属性

obj.propertyIsEnumerable('a'); // true
obj.propertyIsEnumerable('b'); // false
obj.propertyIsEnumerable('c'); // false
obj.propertyIsEnumerable('d'); // true
obj.propertyIsEnumerable(Symbol.for('e')); // true
obj.propertyIsEnumerable(Symbol.for('f')); // false
 
var p = { ...obj}
p.a // 1
p.b // undefined
p.c // undefined
p.d // 4
p[Symbol.for('e')] // 5
p[Symbol.for('f')] // undefined

3.当 writable 属性设置为 false 时,该属性被称为“不可写的”。它不能被重新赋值。

var obj= {}; // 创建一个新对象
 
Object.defineProperty(obj, 'a', {
  value: 37,
  writable: false
});
 
console.log(obj.a); //  37
obj.a = 25; // No error thrown  不会抛出错误,但是也更改不了这个值,因为这个不是在严格模式下
// (it would throw in strict mode,
// even if the value had been the same)
console.log(obj.a); // 还是37 
 
// strict mode
(function() {
  'use strict';
  var obj= {};
  Object.defineProperty(obj, 'b', {
    value: 2,
    writable: false
  });
  obj.b = 3; // throws TypeError: "b" is read-only
  return obj.b; // returns 2 without the line above
}());

4.如果对象中不存在指定的属性,Object.defineProperty() 会创建这个属性。当描述符中省略某些字段时,这些字段将使用它们的默认值。

var obj= {}; // 创建一个新对象
 
// 在对象中添加一个属性与数据描述符的示例
Object.defineProperty(obj, "a", {
  value : 37,
  writable : true,
  enumerable : true,
  configurable : true
});
 
// 对象 obj拥有了属性 a,值为 37
 
// 在对象中添加一个设置了存取描述符属性的示例
var bValue;
Object.defineProperty(obj, "b", {
  // 使用了方法名称缩写(ES2015 特性)
  // 下面两个缩写等价于:
  // get : function() { return bValue; },
  // set : function(newValue) { bValue = newValue; },
  get() { return bValue; },
  set(newValue) { bValue = newValue; },
  enumerable : true,
  configurable : true
});
 
obj.b; // 38
// 对象 obj拥有了属性 b,值为 38
// 现在,除非重新定义 obj.b,obj.b 的值总是与 bValue 相同
 
// 数据描述符和存取描述符不能混合使用
Object.defineProperty(obj, "conflict", {
  value: 0x9f91102,
  get() { return 0xdeadbeef; } 
});
// 抛出错误 TypeError: value appears only in data descriptors, get appears only in accessor descriptors
  • get: 当我们通过person.name访问name的值时,get将被调用。该方法可以自定义返回的具体值是多少。get默认值为undefined
  • set: 当我们通过person.name = 'Jake'设置name的值时,set方法将被调用。该方法可以自定义设置值的具体方式。set默认值为undefined

 考虑特性被赋予的默认特性值非常重要,通常,使用点运算符和 Object.defineProperty() 为对象的属性赋值时,数据描述符中的属性默认值是不同的

var obj= {};
 
obj.a = 1;
// 默认做了下边这件事,等同于:
Object.defineProperty(obj, "a", {
  value: 1,
  writable: true,
  configurable: true,
  enumerable: true
});
 
 
// 如果这样定义,
Object.defineProperty(obj, "a", { value : 1 });
// 默认做了下边这件事,等同于:
Object.defineProperty(obj, "a", {
  value: 1,
  writable: false,
  configurable: false,
  enumerable: false
});

 注意:不能同时设置value、writable 与 get、set的值。

var person = {}
 
// 通过get与set自定义访问与设置name属性的方式
Object.defineProperty(person, 'name', {
    get: function() {
        // 一直返回TOM
        return 'TOM'
    },
    set: function(value) {
        // 设置name属性时,返回该字符串,value为新值
        console.log(value + ' in set');
    }
})
 
// 第一次访问name,调用get
console.log(person.name)   // TOM
 
// 尝试修改name值,此时set方法被调用
person.name = 'alex'   // alex in set
 
// 第二次访问name,还是调用get
console.log(person.name) // TOM

 请尽量同时设置get、set。如果仅仅只设置了get,那么我们将无法设置该属性值。如果仅仅只设置了set,我们也无法读取该属性的值。

二.Object.defineProperties

当我们想要同时设置多个属性的特性时,需要使用Object.defineProperties

语法:  Object.defineProperties(obj, props)

参数说明:

  • obj:在其上定义或修改属性的对象。
  • props:要定义其可枚举属性或修改的属性描述符的对象。对象中存在的属性描述符主要有两种:数据描述符和访问器描述符(更多详情,请参阅Object.defineProperty())。描述符具有以下键:
  • configurable:true 当且仅当该属性描述符的类型可以被改变并且该属性可以从对应对象中删除。默认为 false
  • enumerable:true 当且仅当在枚举相应对象上的属性时该属性显现。默认为 false
  • value:与属性关联的值。可以是任何有效的JavaScript值(数字,对象,函数等)。默认为 undefined.
  • writable:true当且仅当与该属性相关联的值可以用assignment operator改变时。默认为 false
  • get:作为该属性的 getter 函数,如果没有 getter 则为undefined。函数返回值将被用作属性的值。默认为 undefined
  • set:作为属性的 setter 函数,如果没有 setter 则为undefined。函数将仅接受参数赋值给该属性的新值。默认为 undefined

用法除了格式基本与 Object.defineProperty相同

var person = {}
 
Object.defineProperties(person, {
    name: {
        value: 'Jake',
        configurable: true
    },
    age: {
        get: function() {
            return this.value || 22
        },
        set: function(value) {
            this.value = value
        }
    }
})
 
person.name   // Jake
person.age    // 22

我们可以使用Object.getOwnPropertyDescriptor方法读取某一个属性的特性值。

var person = {}
 
Object.defineProperty(person, 'name', {
    value: 'alex',
    writable: false,
    configurable: false
})
 
var descripter = Object.getOwnPropertyDescriptor(person, 'name');
 
console.log(descripter);  // 返回结果如下
 
descripter = {
    configurable: false,
    enumerable: false,
    value: 'alex',
    writable: false
}
转载自:https://juejin.cn/post/7123836412856254472
评论
请登录