问题描述

我有两个几乎相同的简单 JS Fiddles 调用一个功能选择更改。函数名与这两种情况下的 select ID 相同,但由于某种原因,第一个小提琴工作正常,第二个失败,出现了 JavaScript 错误 is not a function

http://jsfiddle.net/AZkfy/7/ – 在 FF9(Linux),Chromium 16(Linux),IE8(Windows) 中工作正常:

<script>
    function border(border) { alert(border); }
</script>

<select id='border' name='border' onchange='border(this.value)'>
    <option value='foo'>foo</option>
    <option value='bar'>bar</option>
</select>

http://jsfiddle.net/cYVzk/ – 在 FF9(Linux),Chromium 16(Linux),IE8(Windows) 中失败:

<script>
    function border(border) { alert(border); }
</script>

<form>
<select id='border' name='border' onchange='border(this.value)'>
    <option value='foo'>foo</option>
    <option value='bar'>bar</option>
</select>
</form>

首先我不明白为什么第一个工作正常,第二个失败。

第二 – 有关 JS 冲突的功能名称和元素 ID 的 JS 规范或限制?

最佳解决方案

当编程语言与我们现在称之为 DOM API(“Dynamic HTML”) 之间没有任何区别时,这是源自 JavaScript 1.0 到 1.3 的遗留范围链问题。

如果您的表单控件 (这里是 select 元素) 是表单的一部分 (form 元素的后代),则表示 form 元素的 Form 对象在控件的事件处理程序属性中的代码范围链中是第三个值 (第二个是表单控件对象本身,接下来是该代码的变量对象) 。

JavaScript™由 Brendan Eich(然后在 Netscape) 设计为一种易于使用的初学者的编程语言,并且与 HTML 文档 (作为 Sun 的 Java 的补充; 因此是 ever-confusing 的名称) 一起使用。因为在早期的语言和 (Netscape)DOM API 中,DOM API 也是一样,这个 (过) 简化也适用于 DOM API:Form 对象的名称包含在表单中的控件的名称作为其属性指的是对应的表单控件对象。 IOW,你可以写

myForm.border

这是 standards-compliant(W3C DOM Level 2 HTML) 的专有缩写,同样也是 backwards-compatible

document.forms["myForm"].elements["border"]

现在,如果您在表单控件的 event-handler 属性值中使用表单控件的名称,例如

<form …>
  <… name="border" onchange='border(this.value)' …>
</form>

那就像你写的是 half-proprietary 一样

<form …>
  <… name="border" onchange='this.form.border(this.value)' …>
</form>

或 standards-compliant

<form …>
  <… name="border" onchange='this.form.elements["border"](this.value)' …>
</form>

因为潜在的全局 border()函数是在范围链中的 Form 对象 (实现 W3C DOM 中的 HTMLFormElement 接口的对象) 之后的最后的 ECMAScript 全局对象的属性。

但是,border 引用的表单控件对象不可调用 (不实现 ECMAScript-internal [[Call]]方法或实现它,以便在调用时引发异常) 。因此,如果您尝试使用 border(this.value)调用该对象,则会抛出 TypeError 异常,您将在脚本控制台中看到 (例如 「Chromium 16.0.912.77 的开发工具」 中的 「TypeError:border is not a function」)[Developer Build 118311 Linux ]) 。

Netscape 在 20 世纪 90 年代的竞争对手微软必须将该功能复制到 MSHTML DOM,以便为 Netscape 编写的代码也将在 Internet Explorer(3.0) 中运行,并使用 JScript(1.0) 。而且微软的竞争对手将它复制到了 DOM 实现中也是完全相同的。它成为 quasi-standard(现称为 「DOM Level 0」) 的一部分。

然后来到了 DOM Level 2 的 HTML 规范,这是一个持续的努力,以标准化和扩展现有的 DOM 实现的共同特征。 W3C 推荐自 2003-01-09 以来,其 ECMAScript Language Binding 指定 HTMLCollection 的项目可以通过其名称或 ID 使用括号属性访问器语法 []访问,相当于实现 HTMLCollection 接口的对象的 namedItem()方法。

form 表单中的表单控件的元素对象和元素对象分别是 W3C DOM,HTMLDocument::formsHTMLFormElement::elements 中的 HTMLCollection 的项目。但是对于浏览器的向后兼容性,

document.forms["myForm"].elements["myControl"]

需要等同于

document.myForm.myControl

因此,最近 W3C DOM 2 级 HTML 接口的实现,此功能开始适用于 ID(id 属性值) 的元素 (例如可以在 Chromium 中看到) 。

因此,16 年前的 JavaScript™中引入的便捷功能仍然使您像今天的 client-side DOM 脚本中的错误一样。

如果您避免使用与用作 user-defined 函数标识符的表单控件和表单相同的名称或 ID,并且已经用于内置表单属性 (如 actionsubmitreset),那么这不再是一个问题。此外,将函数和其参数之一用作相同的标识符 (将混淆代码放在一边) 是一个坏主意,使得函数对象在函数内无法访问 (函数上下文的变量对象首先在其作用域链中) 。

次佳解决方案

IE 在 ID 的每个 DOM 元素的全局空间中自动保留 var ID = domElement; 。其他一些浏览器采用这种行为。

始终尽量避免使用相同的 ID 和 varnames!或者,在 JS 中使用自己的命名空间来避免冲突。

编辑:

我不知道为什么你的一个例子失败,而另一个失败。这可能是一个简单的执行顺序 – 由包装<form> 引起的发生。

参考文献

注:本文内容整合自 Google/Baidu/Bing 辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛