为什么在“冻结”document.body对象之后,我可以修改它的属性?

时间:2018-04-05 18:55:38

标签: javascript

Object.freeze()

  

无法更改数据属性的值。访问器属性(getter和setter)的工作方式相同(并且仍然给出了您正在更改值的错觉)。请注意,仍然可以修改作为对象的值,除非它们也被冻结。作为对象,可以冻结数组,之后不能更改其元素。也不能添加或删除任何元素。

'use strict'

document.body.innerHTML = "Hello#1";
var frozen = Object.freeze(document.body);

document.body = frozen;
console.log("Same frozen body (First):", document.body === frozen)
console.log(document.body['innerHTML']);

// Why I can modify this property?
document.body.innerHTML = "Hello#2";

console.log("Same frozen body (Second):", document.body === frozen);
console.log(document.body['innerHTML']);

此代码段正在使用gettersetter(此方法失败)

'use strict'

var myObj = {
  a: "Ele#1",
  get innerHTML() {
    return this.a;
  }, 
  set innerHTML(html) {
    this.a = html;
  }
}

myObj = Object.freeze(myObj);
myObj.innerHTML = "Ele";

console.log(myObj);

为什么在“冻结”身体对象后,我可以修改它的属性?例如,innerHTML

我可能缺乏信息,我不知道!

2 个答案:

答案 0 :(得分:3)

innerHTML是一个访问者属性。如果您将document.body调试到控制台并向下钻取原型链,您可以在Element中将其定义为get innerHTML()set innerHTML()

这是一个如何实现同样目标的准系统示例:

var myObj = (function() {
    var html = null;

    return {
        get innerHTML() { return html; },
        set innerHTML(value) { html = value; }
    };
})();

答案 1 :(得分:2)

  

访问者属性(getter和setter)的工作方式相同(并且仍然给出了您正在更改值的错觉)。

innerHTML确实是一个访问者属性,如下所示,in the specification



function getPropertyDescriptor(object, property) {
  while (typeof object === 'object' && object !== null) {
    const descriptor = Object.getOwnPropertyDescriptor(object, property)
    if (descriptor) {
      return descriptor
    }
    
    object = Object.getPrototypeOf(object)
  }
}

const desc = getPropertyDescriptor(document.body, 'innerHTML')

console.log(desc)




修改简化示例以使用WeakMap代替属性,您可以使其有效:



'use strict'

const Element = (() => {
  const weakPrivate = new WeakMap()
  
  return class Element {
    constructor () {
      weakPrivate.set(this, { innerHTML: 'initial' })
    }
    
    get innerHTML () {
      return weakPrivate.get(this).innerHTML
    }
    
    set innerHTML (value) {
      return weakPrivate.get(this).innerHTML = value
    }
  }
})()

let object = new Element()

Object.freeze(object)
console.log(object.innerHTML)
object.innerHTML = 'new value'
console.log(object.innerHTML)