语法

  1. Object.defineProperty(obj, prop, descriptor)
  2. - obj
  3. 要定义属性的对象。
  4. - prop
  5. 要定义或修改的属性的名称或 Symbol 。
  6. - descriptor
  7. 要定义或修改的属性描述符
  • configurable
    当且仅当该属性的 configurable 键值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。 默认为 false。

  • enumerable
    当且仅当该属性的 enumerable 键值为 true 时,该属性才会出现在对象的枚举属性中。 默认为 false。

数据描述符还具有以下可选键值:

  • value
    该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。 默认为 undefined。

  • writable
    当且仅当该属性的 writable 键值为 true 时,属性的值,也就是上面的 value,才能被赋值运算符改变。 默认为 false。

存取描述符还具有以下可选键值:

  • get
    属性的 getter 函数,如果没有 getter,则为 undefined。当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入 this 对象(由于继承关系,这里的this并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值。 默认为 undefined。

  • set
    属性的 setter 函数,如果没有 setter,则为 undefined。当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象。 默认为 undefined。

描述符默认值汇总

  • 拥有布尔值的键 configurable、enumerable 和 writable 的默认值都是 false。
  • 属性值和函数的键 value、get 和 set 字段的默认值为 undefined。

描述符可拥有的键值

configurable enumerable value writable get set
数据描述符 可以 可以 可以 可以 不可以 不可以
存取描述符 可以 可以 不可以 不可以 可以 可以

如果一个描述符不具有 value、writable、get 和 set 中的任意一个键,那么它将被认为是一个数据描述符。如果一个描述符同时拥有 value 或 writable 和 get 或 set 键,则会产生一个异常。

函数的第三个参数 descriptor 所表示的属性描述符有两种形式:数据描述符和存取描述符。

两者均有:configurable 和 enumerable 两个键值

数据描述符同时具有以下可选键值:value 和 writable

存取描述符同时具有以下可选键值:get 和 set

PS: 属性描述符必须是数据描述符或者存取描述符两种形式之一,不能同时是两者

可以的方式1:

  1. Object.defineProperty({}, "num", {
  2. value: 1,
  3. writable: true,
  4. enumerable: true,
  5. configurable: true
  6. });

可以的方式2:

  1. var value = 1;
  2. Object.defineProperty({}, "num", {
  3. get : function(){
  4. return value;
  5. },
  6. set : function(newValue){
  7. value = newValue;
  8. },
  9. enumerable : true,
  10. configurable : true
  11. });

不可以的方式:

  1. // 报错
  2. Object.defineProperty({}, "num", {
  3. value: 1,
  4. get: function() {
  5. return 1;
  6. }
  7. });

记住,这些选项不一定是自身属性,也要考虑继承来的属性。为了确认保留这些默认值,在设置之前,可能要冻结 Object.prototype,明确指定所有的选项,或者通过 Object.create(null) 将 __proto__ 属性指向 null。

实现一个自存档对象

  1. // 当设置temperature 属性时,archive 数组会收到日志条目。
  2. function Archiver() {
  3. var temperature = null;
  4. var archive = [];
  5. Object.defineProperty(this, 'temperature', {
  6. get: function() {
  7. console.log('get!');
  8. return temperature;
  9. },
  10. set: function(value) {
  11. temperature = value;
  12. archive.push({ val: temperature });
  13. }
  14. });
  15. this.getArchive = function() { return archive; };
  16. }
  17. var arc = new Archiver();
  18. arc.temperature; // 'get!'
  19. arc.temperature = 11;
  20. arc.temperature = 13;
  21. arc.getArchive(); // [{ val: 11 }, { val: 13 }]

简版数据劫持

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title></title>
  5. </head>
  6. <body>
  7. <span id="container">1</span>
  8. <button id="button">点击加 1</button>
  9. <script type="text/javascript">
  10. var obj = {
  11. value: 1
  12. }
  13. var value = 1
  14. Object.defineProperty(obj,'value',{
  15. get: function() {
  16. return value
  17. },
  18. set: function(newValue) {
  19. value = newValue
  20. document.getElementById("container").innerHTML = newValue
  21. }
  22. })
  23. document.getElementById("button").addEventListener("click",function() {
  24. obj.value += 1
  25. })
  26. </script>
  27. </body>
  28. </html>

watch数据劫持

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title></title>
  5. </head>
  6. <body>
  7. <span id="container">1</span>
  8. <button id="button">点击加 1</button>
  9. <script type="text/javascript">
  10. (function(){
  11. var root = this;
  12. function watch(obj, name, func){
  13. var value = obj[name];
  14. Object.defineProperty(obj, name, {
  15. get: function() {
  16. return value;
  17. },
  18. set: function(newValue) {
  19. value = newValue;
  20. func(value)
  21. }
  22. });
  23. if (value) obj[name] = value
  24. }
  25. this.watch = watch;
  26. })()
  27. var obj = {
  28. value: 1
  29. }
  30. watch(obj, "value", function(newvalue){
  31. document.getElementById('container').innerHTML = newvalue;
  32. })
  33. document.getElementById('button').addEventListener("click", function(){
  34. obj.value += 1
  35. });
  36. </script>
  37. </body>
  38. </html>