为什么不能自动删除从DOM生成的Javascript变量?

5

也许不是常识,但"现代浏览器上的Javascript似乎会为具有ID的DOM元素在window对象上创建变量"

了解这一点后,我希望能够删除这些变量,下面是我尝试过但没有成功的代码。同时请考虑我的控制台.log语句的截图,其中第一个指示why不是window的属性(它应该出现在"webkitUrl"和"window"之间),但是在紧随其后的两个console.log语句中,window/why被显示为文档中的div?

为什么这些自动生成的变量不能像其他变量一样从其父对象中删除?

<!DOCTYPE html>

<html>
<head>
    <script>
    setTimeout(function() { //poor man's document/ready
        var allElements = document.getElementsByTagName("*"), elementId;

        for (var i=allElements.length; i--; ) {
            elementId = allElements[i].id;

            if (elementId && window[elementId] instanceof HTMLElement) {
                delete window.why;
                console.log(window);
                console.log(window.why);
                console.log(why);
            }
        }
    });
    </script>
</head>

<body>
<div id="why"></div>
</body>

</html>

enter image description here


3
1)你为什么想那样做? 2)non-configurable属性无法被删除。 - 4castle
1
有趣的是在Firefox浏览器中, Object.getPrototypeOf(Object.getPrototypeOf(window)).hasOwnProperty("foobar") 的返回值为 true,并且.getOwnPropertyDescriptor()将其列为可配置和可写的,但我不能删除它或者向它写入。 - user1106925
1
@squint 有趣的是,在我用Chrome测试中,它确实删除了它(返回true),但之后仍然存在。JSFiddle演示 - 4castle
1
@squint 没错,在那之后它仍然存在。对于其他人,我认为这会导致在严格模式下不知道是否已经分配了 why ,但如果您尝试将其分配给 why,它确实会发出警告。 - Dexygen
@GeorgeJempty:不幸的是,Firefox在严格模式下不会报错。但是,您可以通过分配只读属性来解决这个问题。Object.defineProperty(window, "foobar", {value: undefined})。虽然会有不同的错误,但总比没有好。 - user1106925
显示剩余7条评论
2个回答

5

这是因为这些属性并没有直接存储在window中,而是像一个代理一样行事。

例如,当您在WindowProperties上使用getOwnPropertyDescriptor时(从中window继承),请参见Firefox的操作

bool WindowNamedPropertiesHandler::getOwnPropDescriptor(
  JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
  bool /* unused */, JS::MutableHandle<JS::PropertyDescriptor> aDesc
) const {
  // ...
  Element* element = document->GetElementById(str);
  if (element) {
    JS::Rooted<JS::Value> v(aCx);
    if (!WrapObject(aCx, element, &v)) {
      return false;
    }
    FillPropertyDescriptor(aDesc, aProxy, 0, v);
    return true;
  }
  // ...
}

你可能认为当你给元素设置一个ID时,Firefox会将其存储为全局属性。但实际上并非如此:只有在尝试访问该属性时,Firefox才会使用GetElementById来确定是否有该ID的元素,并相应地作出回应。
更有甚者,删除是明确禁止的:
bool WindowNamedPropertiesHandler::delete_(
  JSContext* aCx, JS::Handle<JSObject*> aProxy,
  JS::Handle<jsid> aId, JS::ObjectOpResult &aResult
) const {
  return aResult.failCantDeleteWindowNamedProperty();
}

这种行为是硬编码的,你无法阻止它。因此,如果这些属性让你感到烦恼,只需通过声明自己的变量来覆盖它们。


4

我希望能够删除这些变量

没有任何理由这样做。这些全局变量存在于向后兼容性,浏览器几乎不会删除它们。但是,它们是以一种方式创建的,因此您可以简单地忽略它们-它们不会干扰任何您的变量。

只需在脚本中声明 var why;,元素的引用就消失了。

为什么不能从其父对象中自动删除这些生成的变量?

首先,通常情况下无法删除变量。只有对象属性可以被删除-只是一些全局变量是全局对象的可删除属性。

元素引用可能无法删除,因为a)它们不是真正的属性而是代理b)它们不是全局对象本身的属性,而是其原型的属性或c)它们是全局作用域中额外变量记录的属性。只需将它们视为魔术并不再关心即可。


变量(如果没有被引用)应该被垃圾回收。不是吗? - Tuvia
@Tuvia:如果它们是全局的,那么就不是这样,因为你永远不知道什么时候会引用它们。 - Bergi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接