我在项目中使用Require.js.如果要定义模块,则将变量define
添加到全局命名空间。
最近我有一个变量名称冲突[但奇怪的是,仅在Safari中],因为有一个ID为define
的HTML标记,并且根据HTML5 specification:
Window界面支持命名属性。随时支持的属性名称包括以下内容:
- ...
- ...
- 活动文档中具有非空ID内容属性的任何HTML元素的id内容属性的值。
我认为用每个具有ID的HTML元素污染JS全局命名空间是一个糟糕的主意,但仅凭我的观点还不足以改变浏览器的行为方式。
在我的特定实例中,我发现this answer将包装Require.js;但从更广泛的意义上说:有没有办法防范规范中的这个细节?有没有一种技术可以防止这种GNS污染(没有明显的答案:“不要使用ID”)?至少可以通过JS控制台向开发人员(可能不是我)明白冲突吗?
答案 0 :(得分:1)
HTML5的这种行为对RequireJS本身没有影响。我在Firefox和Chrome中运行了以下内容。正如预期的那样,全局空间中的define
符号是RequireJS的定义函数。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/xhtml; charset=utf-8"/>
</head>
<body>
<p id="define">Paragraph.</p>
<button id="redefine">Redefine</button>
<script type="text/javascript">
console.log("before requirejs:", typeof window.define);
</script>
<script type="text/javascript" src="js/require.js"></script>
<script>
require.config({
baseUrl: "./js",
paths: {
jquery: 'jquery-1.10.2'
},
});
console.log("requirejs loaded:", typeof window.define);
require(["jquery"], function ($) {
$("#redefine").click(function () {
$("#define").replaceWith("<p id='define'>New paragraph.</p>");
console.log("redefined:", typeof window.define);
require(["foo"], function (foo) {
console.log(foo);
});
});
});
</script>
</body>
</html>
我使用typeof
来避免转储整个函数源。这足以达到我们的目的。 “重新定义”按钮用于模拟对DOM树的更改,人们可能认为该更改会覆盖window.define
的值。 (模块foo
是一个仅返回值"foo"
的trival模块。)单击处理程序中的require
表示RequireJS仍然有效。使用上面的代码,控制台的输出显示:
即使最初的window.define
是DOM元素,RequireJS也会将define
重新定义为自己的值。
一旦RequireJS定义了define
,对DOM树的更改就不会改变它。
所以RequireJS不需要受到保护。
但是那些坚持使用window.define
来访问id为“define”的元素的代码呢?这样的代码有缺陷,应该修复。这个SO question很好地解决了这个问题。即使要求RequireJS避免触及全局define
,依赖于window.define
作为DOM元素的代码仍然会很脆弱。
是否有防止此GNS污染的技术(没有明显答案:“不使用ID”)?
除了明显的答案,没有。 (我使用“使用ID以外的东西”等同于“不使用ID”。)
至少可以通过JS控制台向开发人员(可能不是我)发现冲突吗?
JS虚拟机可能会检测到冲突,但我不知道是否会发生这种情况。
这是Safari在JavaScript中处理全局变量的一个错误。没有其他浏览器能像Safari那样表现出来。
This repository包含一组说明问题的文件。如果在Safari中加载,index.html
文件将在控制台上显示失败。它在Firefox,Chrome,IE和Opera中运行良好。 test.html
文件确定了问题。除Safari之外的所有浏览器都会在顶级作用域中将var foo
之类的JavaScript中的变量声明视为遮蔽由于具有id
属性集的元素而创建的同名变量。在除Safari之外的任何浏览器中加载,输出console.log
的{{1}}将显示start of redefine:
的变量值。在Safari中,它显示undefined undefined
。
问题has been filed与RequireJS建议进行更改以解决此问题。
如果建议的更改未通过,则解决方法是在加载RequireJS之前执行p#define p#define
。没有必要再做任何事了。