問題描述

我有兩個幾乎相同的簡單 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 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇