Object.defineProperty()Object.defineProperties()都是Object构造函数的静态方法,作用是定义数据!!!

defineProperty()

:::info 参数1: 需要被定义的对象
参数2: 需要被定义的对象属性
参数3: 描述配置对象 :::

  1. // 假如我现在要对 obj 对象的 test 属性进行定义
  2. var obj = {};
  3. Object.defineProperty(obj, "test", {
  4. value: 1 // value 属性表示 test 属性的值,后面还会讲到其他的配置属性
  5. });
  6. console.log(obj); // {test: undefined}

以上代码的obj从明面上看和字面量定义的对象没有任何的区别:

  1. var obj = {
  2. test: undefined
  3. }
  4. console.log(obj); // {test: undefined}

属性配置

可事实真的如此吗?下面我们将两种方式对象进行对比。

  1. var obj1 = {a: 1};
  2. var obj2 = Object.defineProperty({}, "b", {
  3. value: 2
  4. });
  5. console.log(obj1); // {a: 1}
  6. console.log(obj2); // {a: 2}

这样我们就生成了obj1obj2两个对象。
我们想两个对象的属性值进行修改尝试:

  1. obj1.a = 2;
  2. obj2.b = 3;
  3. console.log(obj1); // {a: 2}
  4. console.log(obj2); // {a: 2}

结果:使用Object.defineProperty()定义的属性无法更改属性值。

然后我们去遍历两个对象:

  1. for (const key in obj1) {
  2. console.log(obj1[key]);
  3. }
  4. // 1
  5. for (const key in obj2) {
  6. console.log(obj2[key]);
  7. }
  8. // 无任何输出

结果:使用Object.defineProperty()定义的属性无法枚举 枚举就是将项列举出来。

最后我们去删除两个对象的属性:

  1. delete obj1.a;
  2. delete obj2.b;
  3. console.log(obj1); // {}
  4. console.log(obj2); // {b: 2}

结果:使用Object.defineProperty()定义的属性无法删除

:::info 🌴 总结:
使用Object.defineProperty()定义的属性无法更改、删除和遍历。 :::

到现在我们发现Object.defineProperty()的参数 3 一直没有配置,其实参数 3 是个对象用于对对象的属性进行相关的配置:

  1. var obj = Object.defineProperty({}, "test",{
  2. value: 1, // 配置属性的值
  3. writable: true, // 配置属性是否可以被更改,默认是 false
  4. enumerable: true, // 配置属性是否可以被枚举,默认是 false
  5. configurable: true // 配置属性是否可以被删除,默认是 false
  6. })
  7. // 然后在对 obj 的属性进行操作
  8. console.log(obj); // {test: 1}
  9. obj.test = 123;
  10. console.log(obj); // {test: 123}
  11. for (const key in obj) {
  12. console.log(obj[key]);
  13. }
  14. // 123
  15. delete obj.test;
  16. console.log(obj); // {}

get()/set()

每个属性在定义的时候都会存在gettersetter的机制,所以Object.defineProperty()的参数 3 还有set()get()方法。

  1. var value = 1;
  2. var obj = Object.defineProperty({}, "test",{
  3. get: function(){
  4. console.log("访问 obj.test 属性")
  5. return value;
  6. },
  7. set: function(newVal){
  8. console.log("设置 obj.test 属性")
  9. value = newVal;
  10. }
  11. })
  12. console.log(obj.test);
  13. // 访问 obj.test 属性
  14. // 1
  15. obj.test = 456;
  16. // 设置 obj.test 属性
  17. console.log(obj.test);
  18. // 访问 obj.test 属性
  19. // 2
  20. consoloe.log(obj);

image.png
这里可以发现test属性是展开三角后才能看到的,猜测这是因为浏览器要和普通属性作区分特意而为之。

利用getset方法就可以实现对对象属性的数据劫持!!!

  1. var obj = {};
  2. Object.defineProperty(obj, "a", {
  3. get() {
  4. return "this is a";
  5. },
  6. set(newVal) {
  7. // 当我们对象 obj.a 进行设置时候,p 元素的内容也会发生变化
  8. var oP = document.getElementsByTagName("p")[0];
  9. oP.innerHTML = newVal;
  10. }
  11. });

defineProperty()本身是Object构造函数的静态方法,所以该方法本身只可以对对象具备劫持,那么如何对数组也进行劫持呢?

  1. function DateArr() {
  2. var _arr = [],
  3. _val = null;
  4. Object.defineProperty(this, "val", {
  5. get: function () {
  6. return _val;
  7. },
  8. set: function (newVal) {
  9. _val = newVal;
  10. _arr.push({ val: _val });
  11. console.log("A new value " + _val + "has been pushed to _arr");
  12. },
  13. });
  14. this.getArr = function () {
  15. return _arr;
  16. };
  17. }
  18. var dateArr = new DateArr();
  19. dateArr.val = 123;
  20. dateArr.val = 234;
  21. console.log(dateArr.getArr()); // [{val: 123}, {val: 234}]

互斥

:::danger 注意 ⚠️
如果配置中只有enumerable``configurable的时候该配置是对数据描述。
如果配置中同时存在 「valuewritable」和 「getset」 是互斥的!!! :::

  1. var obj = {};
  2. // 以下的情况会发生错误
  3. var obj1 = Object.defineProperty({}, "a" ,{
  4. value: 1, // error
  5. writable: true, // error
  6. get() {
  7. return "this is a";
  8. },
  9. set(newVal) {
  10. var oP = document.getElementsByTagName("p")[0];
  11. oP.innerHTML = newVal;
  12. }
  13. });
  14. // 以下的情况会正常执行
  15. var obj2 = Object.defineProperty({}, "a" ,{
  16. value: 1,
  17. writable: true,
  18. enumerable: true,
  19. configurable: true,
  20. });
  21. // 以下的情况也会正常执行
  22. var obj3 = Object.defineProperty({}, "a" ,{
  23. enumerable: true,
  24. configurable: true,
  25. get() {
  26. return "this is a";
  27. },
  28. set(newVal) {
  29. var oP = document.getElementsByTagName("p")[0];
  30. oP.innerHTML = newVal;
  31. }
  32. });

defineProperties()

Object.defineProperty()方法只能对对象的一个属性进行定义,那么如何定义对象的多个属性呢?
可以使用Object.defineProperties()来定义多个属性。

defineProperties()defineProperty()写法的不同在于defineProperties接收两个参数,参数1 是要定义的对象,参数 2 是定义对象属性的对象。

  1. var value = 2;
  2. var obj = Object.defineProperties({}, {
  3. a: {
  4. value: 1,
  5. writable: true,
  6. enumerable: true,
  7. configurable: true,
  8. },
  9. b: {
  10. get() {
  11. return value;
  12. },
  13. set(newVal) {
  14. value = newVal;
  15. },
  16. }
  17. });
  18. console.log(obj);

image.png