问题:全局只读属性
首先,请注意这个问题存在于Web环境中,即浏览器中。
其他JS环境可能没有这个问题。
var closed = 0;
在全局范围内不会创建引用数字的绑定,并且仍然是布尔值false
.
这样做的原因是closed
指的是window.closed在这种情况下;它始终是一个布尔值,重新定义它会产生一个 linter 警告,例如“重新定义closed
”.
This 只读属性指示引用的窗口是否关闭。
可以在以下列表中找到具有类似行为的变量名称:
- Window特性
- WorkerGlobalScope特性
MDN 上也曾经有过关于 mixin 的文档,名为WindowOrWorkerGlobalScope
,其属性适合此列表;然而,这仅指的是两者都存在的事物Window
and in WorkerGlobalScope
。
一小段上下文:MDN 对其文档进行了重构,删除了所有这些“混合”以支持标准接口名称。
你可以找到此列表的存档版本.
Window
or WorkerGlobalScope
其中两个接口globalThis
可以是 Web 环境中的实例。
运行代码片段以获取无法在全局范围内安全使用的变量名称的完整列表:
const props = Object.entries(Object.getOwnPropertyDescriptors(globalThis)),
undesirable = {
set: (desc) => desc,
configurable: (desc) => !desc,
writable: (desc) => !desc
};
Array.from(document.querySelectorAll("[id]"))
.forEach((span) => span.innerHTML = props
.filter(([prop, {[span.id]: desc}]) => undesirable[span.id](desc))
.map(([prop]) => `<code>${prop}</code>`)
.join(", "))
code{
background: #eee;
padding: 1px 3px;
}
<p>Properties that have a setter which may change the type or invoke some function, when a value is set to it:</p>
<span id="set"></span>
<hr/>
<p>Properties that are not configurable:</p>
<span id="configurable"></span>
<hr/>
<p>Properties that are read-only:</p>
<span id="writable"></span>
你会注意到,这是一个lot。
还有很多简短的常见变量名,例如name, length
[1], [2], status
[1], [2], self
, top, menubar
, and parent。
此外,关于在分配给 setter 时调用某些函数,例如var location = "Podunk, USA";
实际上重定向你到那个地点./Podunk, USA
.
使用函数名称存在一个相关问题,例如lang, checked, autocomplete, evaluate, or animate在事件属性中,例如onclick
: 不仅有window
属性包含在作用域链中,但也包含所有scopable当前整个原型链的属性HTMLDocument
以及目前的具体Element
(例如使用<a onclick=""></a>
提供对所有内容的访问HTMLAnchorElement.prototype
)也许还有更多,例如任何来自form
(表单元素的)属性(如果存在)。
这一切也都在这篇文章中得到了解释回答相关问题.
当依赖时也会出现这个问题DOM 节点带有id成为全球地产.
解决方案
对此有一些解决方案。
我将按照从最好到最差的主观顺序列出它们。
使用模块代替
将您的 JavaScript 代码运行为modules代替脚本不仅是新的很酷的事情,而且只是没有这个问题.
index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Your site</site>
<script type="module" src="main.mjs"></script>
</head>
<body>
<!-- Content. -->
</body>
</html>
main.mjs
:
const top = { spinning: true },
window = { type: "clear", frame: "wood" };
let document = { type: "LaTeX", content: "\\documentclass[a4]{…} …" },
closed = 0;
var location = "Podunk, USA";
// All of these are the variables defined above!
console.log({ top, window, document, closed, location });
模块还带有一些其他有用的功能,例如隐式延期和---关联DOM解析.
将一切包裹在新的范围内
在脚本的全局作用域中,您会遇到这个问题。
在新的函数作用域中,您不会遇到此问题:
(function(){
const closed = 0; // Or `let` or `var`.
$(function(){
console.log(closed);
});
})();
您还可以创建更有用的范围,例如 jQuery 的$(function(){
…});
它等待 DOM 被加载。This顺便说一句,可能是兼容性最高的选项,同时仍然是更好的选项之一。
因为我不使用 jQuery,所以我更喜欢将所有内容包装在DOMContentLoaded
侦听器,我可以在其中确定我需要的所有变量的范围,此外,还可以使用"use strict";
:
addEventListener("DOMContentLoaded", () => { "use strict";
// Code goes here.
});
这避免了全局属性和全局变量之间的冲突。
总是使用const
。如果您无法使用const
, use let
。如果您无法使用let
, use var
.
… But you’ll never need var
!1
Using const
or let
在脚本中,如安基特·阿加瓦尔的回答建议,缓解这个问题:
const closed = 0;
console.log(closed); // 0
然而,这仅适用于most变量名,但不是全部 —const closed = 123; let history = "Hello, world!";
所有工作,但是const window = 123;, const top = 123; or let document;
don’t.
1: Although there may be one edge-case.
And obviously, older browsers still need var
.
只需使用不同的变量名称
听起来很明显,但这并不是很稳健。
新的全局属性可能总是会弹出,因此全局变量可能随时与它们发生冲突。
由于这显然会破坏整个互联网上的多个脚本,因此现在添加的全局属性实际上并不会产生太大的问题。const history = "Regency Era";
工作得很好并且使history
可用作引用该字符串的变量。var history =
…;
仍然没有,并且它会抛出一个错误严格模式,并且在松散模式下默默地失败。这在历史上导致了兼容性问题这是why这不是一个强大的选项,尤其是在使用时var
,这又是反对的另一个有力论据var
.
重新分配older属性,例如document
,将会失败scripts,在某些环境下,但是每个变量名都可以在module,在每种环境中,这就是为什么这是最推荐的选项。