標題: 轉:理解javascript中的delete機制(3)
- 小小蘇 2010-05-17 10:16 閱讀:17
- 評論:0 | 發表評論 | 返回 ↓
IE bugs
整個章節僅僅為了IE中的bug,想不到吧!
在IE瀏覽器中(至少是IE6-IE8),下面的運算式拋出錯誤(在全域代碼中執行):
this.x = 1;
delete x; // TypeError: Object doesn't support this action
這個也是一樣,但異常不同,只是更有趣:
var x = 1;
delete this.x; // TypeError: Cannot delete 'this.x'
IE中看起來好像在全域代碼中聲明變數不能在全域對象中建立屬性。通過賦值建立屬性(this.x = 1),然後通過delete刪除x將拋出錯誤。通過聲明建立建立屬性(var x = 1),然後通過delete this.x刪除將拋出另外一個錯誤。
但這還沒完。實際上通過明確的賦值建立的屬性在刪除時始終引發錯誤。這不僅是一個錯誤,而且建立的屬性似乎設定了DontDelete特性,這當然不應該有:
代碼
1 this.x = 1;
2 delete this.x; // TypeError: Object doesn't support this action
3 typeof x; // "number" (still exists, wasn't deleted as it should have been!)
4 delete x; // TypeError: Object doesn't support this action
5 typeof x; // "number" (wasn't deleted again)
與我們思考的相反,未聲明的變數(應該在一個全域對象中建立屬性)在IE中建立了可刪除屬性。
x = 1;
delete x; // true
typeof x; // "undefined"
但是,如果您嘗試通過“this”引用在全域代碼中刪除它(delete this.x ),一個熟悉的錯誤彈出:
x = 1;
delete this.x; // TypeError: Cannot delete 'this.x'
如果我們總結這些行為,從全域代碼中delete this.x 似乎是不成功的。當涉及到的屬性是通過顯式聲明(this.x = 1 )來建立的,delete 將拋出一個錯誤。當屬性是通過未聲明的賦值(x = 1 )或聲明(var x = 1 )來建立屬性時,delete 將拋出另一個錯誤。
另一方面,當涉及到的屬性是通過顯式聲明(this.x = 1 )建立時,delete x 拋出錯誤。如果一個屬性是通過聲明(var x = 1 )來建立的,刪除根本不會發生,並返回正確的false。如果屬性是通過未聲明的方式(x = 1)建立,刪除操作將按預期進行。
去年九月我正在思考這個問題,Garrett Smith 建議“在IE中全域可變對象作為一個JScript對象,全域對象有宿主執行”。Garrett 引用Eric Lippert’s blog entry ,我們可以通過一些測實驗證這些理論。請注意,this和window似乎引用同一對象(如果我們相信“===”運算子),但可變對象(在一個聲明的函數中的對象)不同於這一點。
代碼
1 /* in Global code */
2 function getBase(){ return this; }
3
4 getBase() === this.getBase(); // false
5 this.getBase() === this.getBase(); // true
6 window.getBase() === this.getBase(); // true 7.window.getBase() === getBase(); // false
誤區
理解事物為什麼那麼工作是一種難以言說的美,我在網上已經看到了與delete運算子誤解相關的誤區。例如,在關於棧溢出的回答(評分出其不意的效果高)中,它自信的解釋道:“delete is supposed to be no-op when target isn’t an object property ”。現在,我們已經理解了delete 行為的核心,很清楚這個答案是不準確的。delete 不區分變數和屬性(事實上,對於刪除,這些都是引用),真正的只關心的是DontDelete特性(和屬性存在)。
非常有意思的看到這個誤解如何相互影響,在同樣一個線程中,有人首先提出要直接刪除變數(除非它是在eval中聲明,否則不會生效),接著另外一個人提出一種錯誤的糾正方法--在全域中可能刪除變數,但在函數內不行。
在網站上解釋Javascript 最好小心,最好總是抓住問題的核心。
‘delete’和宿主對象
delete 的演算法大概是這樣:
- 如果操作不是一個引用,返回
true;
- 如果一個對象沒有直接的屬性,返回
true;(我們知道,對象可以是啟用物件,可以是全域象);
- 如果一個屬性存在並有DontDelete特性,返回
false;
- 否則,刪除屬性並返回
true;
但是,宿主對象的delete 運算子的行為難以預測。實際上並沒有錯:除了少數幾個,宿主對象是允許執行任何類型的運算行為的(按規範),如read(內部的[get]方法)、write(內部的[put]方法)或delete(內部的[delete]方法)。這個定製的[[Delete]]行為使得宿主對象如此混亂。
在IE中我們已經看到一些古怪的行為,如果刪除某些對象(明顯作為宿主對象來執行)將拋出錯誤。Firefox的一些版本在嘗試刪除window.location時將拋出錯誤。當涉及到宿主對象時,你不能信任delete返回的任何值。看看在Firefox會有什麼發生:
1 /* "alert" is a direct property of `window` (if we were to believe `hasOwnProperty`) */
2 window.hasOwnProperty('alert'); // true
3 delete window.alert; // true
4 typeof window.alert; // "function"
刪除window.alert 返回true,雖然這個屬性什麼也沒有,它應該導致這個結果。它保留了一個引用(因此在第一步中不應該返回true),它是視窗對象的直接屬性(因此第二步中不能返回true)。唯一的辦法讓delete返回true是在第四步之後真正刪除屬性。但是,屬性是永遠不會被刪除的。
這個故事的寓意在於永遠不要相信宿主對象
ES5strict 模式
那麼,ECMAScript 5th edition 的strict 模式可以拿到檯面上來了。一些限制正被引入,當delete運算子是一個變數、函數參數或函數標識符的直接引用時將拋出SyntaxError。另外,如果屬性內部有[[Configurable]] == false,將拋出TypeError。
代碼
1 (function(foo){
2 "use strict"; // enable strict mode within this function
3 var bar;
4 function baz(){}
5 delete foo; // SyntaxError (when deleting argument)
6 delete bar; // SyntaxError (when deleting variable)
7 delete baz; // SyntaxError (when deleting variable created with function declaration)
8 /* `length` of function instances has { [[Configurable]] : false } */
9 delete (function(){}).length; // TypeError
10 })();
另外,刪除未聲明的變數(換句話說,沒有找到的引用)也拋出SyntaxError。
"use strict";
delete i_dont_exist; // SyntaxError
正如你所理解的那樣,考慮到刪除變數、函式宣告和參數會導致如此多得混淆,所有這些限制就有點意義。與不聲不響的忽略刪除行為相反,strict 模式應該採取更積極的、更具有描述性的措施。
總結
這篇文章是冗長的,我打算去討論用delete刪除數組選項和它的含義。你可以隨時參考MDC 的文章瞭解具體的解釋(或閱讀規範,自己實驗)。
這是Javascript中delete運算子工作的簡短概要:
- 變數和函式宣告要麼是啟用物件的屬性,要麼是全域對象的屬性;
- 屬性有一些特性,其中之一就是DontDelete,它決定一個屬性是否能刪除;
- 在Global 和Function code 中的變數和函式宣告總是有DontDelete特性;
- 函數參數也是啟用物件的屬性,具有DontDelete特性;
- 在Eval代碼中的變數和函式宣告總是建立沒有DontDelete特性的屬性;
- 新的屬性總是帶有空的特性(因此沒有DontDelete特性);
- 宿主對象允許對刪除作出反應,無論它們是否願意如此;
如果你想瞭解更多這裡這裡描述的東西,請參閱ECMA-262 3rd edition specification。
我希望你喜歡這篇綜述,並能學到新東西。任何疑問、建議、更正,一律歡迎。
相關閱讀:
- delete Operator
- delete Operator (Windows Scripting – JScript)
原文地址:Understanding delete
轉載地址:http://www.denisdeng.com/?p=858
發表評論
返回頂部 | 返回首頁
上一篇:轉:理解javascript中的delete機制(2)
下一篇:在 .NET Framework 2.0 中未處理的異常導致基於 ASP.NET 的應用程式意外退出
完整模式(顯示圖片和代碼)
查看原文