Javascript的變量與delete操作符

剛剛看到一篇好文, 對Javascript中的delete操作符分析得很透徹。在這裡簡單地介紹一下內容。
 
雖然是一個小小的delete操作符,其行為卻異常復雜。
 
Javascript的變量
delete操作符刪除的對象
對變量執行delete的情況
能刪除的屬性和不能刪除的屬性
能刪除的變量和不能刪除的變量
delete的返回值
Javascript的變量
實際上Javascript中,變量= 對象屬性,這是因為Javascript 在執行腳本之前 會創建一個Global對象,所有的全局變量都是這個Global對象的屬性, 執行函數時也會創建一個Activation對象,所有的局部變量都是這個Activation對象的屬性。 如下例:
 
var global = 42;
this.global;    // 42, 可以通過this來訪問Global對象
 
this.global2 = 12;
global2;        // 12
 
function foo() {
  var local = 36;
  // 不過無法直接訪問Activation,
  // 因此無法通過foo.local 的方式來訪問local變量
}
delete操作符刪除的對象
C++中也有delete操作符,它刪除的是指針所指向的對象。例如:
 
// C++
class Object {
public:
  Object *x;
}
 
Object o;
o.x = new Object();
delete o.x;     // 上一行new的Object對象將被釋放
但Javascript的delete與C++不同,它不會刪除o.x指向的對象,而是刪除o.x屬性本身。
 
// Javascript
var o = {};
o.x = new Object();
delete o.x;     // 上一行new的Object對象依然存在
o.x;            // undefined,o的名為x的屬性被刪除瞭
在實際的Javascript中,delete o.x之後,Object對象會由於失去瞭引用而被垃圾回收, 所以delete o.x也就“相當於”刪除瞭o.x所指向的對象,但這個動作並不是ECMAScript標準, 也就是說,即使某個實現完全不刪除Object對象,也不算是違反ECMAScript標準。
 
“刪除屬性而不是刪除對象”這一點,可以通過以下的代碼來確認。
 
var o = {};
var a = { x: 10 };
o.a = a;
delete o.a;    // o.a屬性被刪除
o.a;           // undefined
a.x;           // 10, 因為{ x: 10 } 對象依然被a 引用,所以不會被回收
另外,delete o.x 也可以寫作delete o["x"],兩者效果相同。
 
對變量執行delete的情況
由於變量也是Global 或者是Activation 對象的屬性,所以對變量的delete操作也是同樣的結果。
 
var global = 42;
delete global;     // 刪除Global.global
 
function foo() {
  var local = 36;
  delete local;    // 刪除Activation.local
}
能刪除的屬性和不能刪除的屬性
並不是所有的屬性都能被delete。例如,prototype中聲明的屬性就無法被delete:
 
function C() { this.x = 42; }
C.prototype.x = 12;
 
var o = new C();
o.x;     // 42, 構造函數中定義的o.x
 
delete o.x;
o.x;     // 12,  prototype中定義的o.x,即使再次執行delete o.x也不會被刪除
對象的預定義屬性也無法刪除。 可以認為這類屬性帶有DontDelete的特性。
 
var re = /abc/i;
delete re.ignoreCase;
re.ignoreCase; // true, ignoreCase無法刪除
能刪除的變量和不能刪除的變量
通過var聲明的變量和通過function聲明的函數擁有DontDelete特性,無法被刪除。
 
var x = 36;
delete x;
x;     // 36, x沒有被刪除
 
y = 12;
delete y;
y;     // undefined
 
function foo() { return 42; }
delete foo;
foo();  // 42
但是有一點例外,就是通過eval 執行的代碼中,通過var聲明的變量雖然與正常的var聲明變量 同屬於Global對象,但它們不具有DontDelete特性,能被刪除。
 
eval("var x = 36;");
x;     // 42
delete x;
x;     // undefined
但是這也有一點例外,eval的代碼中的函數內通過var定義的變量具有DontDelete,不能被刪除。
 
eval("(function() { var x = 42; delete x; return x; })();");
// 返回42
delete的返回值
delete是普通運算符,會返回true或false。規則為:當被delete的對象的屬性存在並且擁有DontDelete時 返回false,否則返回true。 這裡的一個特點就是,對象屬性不存在時也返回true,所以返回值並非完全等同於刪除成功與否。
 
function C() { this.x = 42; }
C.prototype.y = 12;
var o = new C();
 
delete o.x; // true
o.x;        // undefined
"x" in o;   // false
// o.x存在並且沒有DontDelete,返回true
 
delete o.y; // true
o.y;        // 12
// o自身沒有o.y屬性,所以返回true
// 從這裡也可以看到prototype鏈的存在,對象自身屬性和prototype屬性是不同的
 
delete o;   // false
// Global.o擁有DontDelete特性所以返回false
 
delete undefinedProperty;  // true
// Global沒有名為undefinedProperty的屬性因此返回true
 
delete 42;  // true
// 42不是屬性所以返回true。有的實現會拋出異常(違反ECMAScript標準)
 
var x = 24;
delete x++;  // true
x;           // 25
// 被刪除的是x++的返回值(24),不是屬性,所以返回true

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。