Javascript中的delete介紹

來源:互聯網
上載者:User

一、問題的提出

  我們先來看看下面幾段代碼,要注意的是,以下代碼不要在瀏覽器的開發人員工具(如FireBug、Chrome Developer tool)中運行,原因後面會說明:

  為什麼我們可以刪除對象的屬性:
複製代碼 代碼如下:
var o = { x: 1 };
delete o.x; // true
o.x; // undefined  

但不以刪除像這樣聲明的變數:
複製代碼 代碼如下:
var x = 1;
delete x; // false
x; // 1

也不能刪除像這樣定義的函數:
複製代碼 代碼如下:
function x(){}
delete x; // false
typeof x; // "function"

注意:當delete操作符返回true時表示可以刪除,返回false表示不能刪除

  要理解這一點,我們首先需要掌握像變數執行個體化和屬性特性這樣的概念--遺憾的是這些內容在一些javascript的書中很少講到。理解它們並不難,如果你不在乎它們為什麼這麼運行,你可以隨意的跳過這一部分。

二、代碼類型

  在ECMAScript中有三種類型的可執行代碼:Global code(全域代碼)、Function code(函數代碼)和 Eval code(放在Eval中執行的代碼)。
複製代碼 代碼如下:
var x=1;//Global code
function test(){
var y=2;//Function Code
eval("var z=3");//Eval Code in Function
}
eval("function evalTest(){}");//Eval Code in Global


三、執行內容

  當ECMAScript 代碼執行時,它總是在一定的上下文中運行,執行內容是一個有點抽象的實體,它有助於我們理解範圍和變數執行個體化如何工作的。對於三種類型的可執行代碼,每個都有執行的上下文。當一個函數執行時,可以說控制進入到函數代碼(Function code)的執行內容。全域代碼執行時,進入到全域代碼(Global code)的執行內容。

  正如你所見,執行內容邏輯上來自一個棧。首先可能是有自己範圍的全域代碼,代碼中可能調用一個函數,它有自己的範圍,函數可以調用另外一個函數,等等。即使函數遞迴地調用它自身,每一次調用都進入一個新的執行內容。

四、Activation object(啟用物件)/Variable object(變數對象)

  每一個執行內容在其內部都有一個Variable Object。與執行內容類似,Variable object是一個抽象的實體,用來描述變數執行個體化的機制。有趣的是在代碼中聲明的變數和函數實際上被當作這個變數對象的屬性被添加。

  當進入全域代碼的執行內容時,一個全域對象用作變數對象。這也正是為什麼在全域範圍中聲明的變數或者函數變成了全域對象的屬性。
複製代碼 代碼如下:
/* remember that `this` refers to global object when in global scope */
var GLOBAL_OBJECT = this;

var foo = 1;
GLOBAL_OBJECT.foo; // 1
foo === GLOBAL_OBJECT.foo; // true

function bar(){}
typeof GLOBAL_OBJECT.bar; // "function"
GLOBAL_OBJECT.bar === bar; // true

全域變數變成了全域對象的屬性,但是,那些在函數代碼(Function code)中定義的局部變數又會如何呢?行為其實很相似:它成了變數對象的屬性。唯一的差別在於在函數代碼(Function code)中,變數對象不是全域對象,而是所謂的啟用物件(Activation object)。每次函數代碼(Function code)進入執行範圍時,就會建立一個啟用物件(Activation object)。

  不僅函數代碼(Function code)中的變數和函數成為啟用物件的屬性,而且函數的每一個參數(與形參相對應的名稱)和一個特定Arguments 對象也是。注意,啟用物件是一種內部機制,不會被程式碼真正訪問到。
複製代碼 代碼如下:
(function(foo){

var bar = 2;
function baz(){}

/*
In abstract terms,

Special `arguments` object becomes a property of containing function's Activation object:
ACTIVATION_OBJECT.arguments; // Arguments object

...as well as argument `foo`:
ACTIVATION_OBJECT.foo; // 1

...as well as variable `bar`:
ACTIVATION_OBJECT.bar; // 2

...as well as function declared locally:
typeof ACTIVATION_OBJECT.baz; // "function"
*/

})(1);

最後,在Eval 代碼(Eval code)中聲明的變數作為正在調用的內容相關的變數對象的屬性被建立。Eval 代碼(Eval code)只使用它正在被調用的哪個執行內容的變數對象。
複製代碼 代碼如下:
var GLOBAL_OBJECT = this;

/* `foo` is created as a property of calling context Variable object,
which in this case is a Global object */

eval('var foo = 1;');
GLOBAL_OBJECT.foo; // 1

(function(){

/* `bar` is created as a property of calling context Variable object,
which in this case is an Activation object of containing function */

eval('var bar = 1;');

/*
In abstract terms,
ACTIVATION_OBJECT.bar; // 1
*/

})();

五、屬性特性
  現在變數會怎樣已經很清楚(它們成為屬性),剩下唯一的需要理解的概念是屬性特性。每個屬性都有來自下列一組屬性中的零個或多個特性--ReadOnly, DontEnum, DontDelete 和Internal,你可以認為它們是一個標記,一個屬性可有可無的特性。為了今天討論的目的,我們只關心DontDelete 特性。

  當聲明的變數和函數成為一個變數對象的屬性時--要麼是啟用物件(Function code),要麼是全域對象(Global code),這些建立的屬性帶有DontDelete 特性。但是,任何明確的(或隱含的)建立的屬性不具有DontDelete 特性。這就是我們為什麼一些屬效能刪除,一些不能。
複製代碼 代碼如下:
var GLOBAL_OBJECT = this;

/* `foo` is a property of a Global object.
It is created via variable declaration and so has DontDelete attribute.
This is why it can not be deleted. */

var foo = 1;
delete foo; // false
typeof foo; // "number"

/* `bar` is a property of a Global object.
It is created via function declaration and so has DontDelete attribute.
This is why it can not be deleted either. */

function bar(){}
delete bar; // false
typeof bar; // "function"

/* `baz` is also a property of a Global object.
However, it is created via property assignment and so has no DontDelete attribute.
This is why it can be deleted. */

GLOBAL_OBJECT.baz = 'blah';
delete GLOBAL_OBJECT.baz; // true
typeof GLOBAL_OBJECT.baz; // "undefined"


六、內建屬性和DontDelete

  一句話:屬性中一個獨特的特性(DontDelete)控制著這個屬性是否能被刪除。注意,對象的內建屬性(即對象的預定義屬性)有DontDelete 特性,因此不能被刪除。特定的Arguments 變數(或者,正如我們現在瞭解的,啟用物件的屬性),任何函數執行個體的length屬性也擁有DontDelete 特性。
複製代碼 代碼如下:
(function(){

/* can't delete `arguments`, since it has DontDelete */

delete arguments; // false
typeof arguments; // "object"

/* can't delete function's `length`; it also has DontDelete */

function f(){}
delete f.length; // false
typeof f.length; // "number"

})();

與函數參數相對應的建立的屬性也有DontDelete 特性,因此也不能被刪除。
複製代碼 代碼如下:
(function(foo, bar){

delete foo; // false
foo; // 1

delete bar; // false
bar; // 'blah'

})(1, 'blah');


七、未聲明的賦值

  簡單地就是未聲明的賦值在一個全域對象上建立一個可刪除的屬性。
複製代碼 代碼如下:
var GLOBAL_OBJECT = this;

/* create global property via variable declaration; property has DontDelete */
var foo = 1;

/* create global property via undeclared assignment; property has no DontDelete */
bar = 2;//可理解為 window.bar=2; 根據上面的第五點是可以刪除的

delete foo; // false
typeof foo; // "number"

delete bar; // true
typeof bar; // "undefined"

請注意,DontDelete特性是在屬性建立的過程中確定的,後來的賦值不會修改現有屬性已經存在的特性,理解這一點很重要。
複製代碼 代碼如下:
/* `foo` is created as a property with DontDelete */
function foo(){}

/* Later assignments do not modify attributes. DontDelete is still there! */
foo = 1;
delete foo; // false
typeof foo; // "number"

/* But assigning to a property that doesn't exist,
creates that property with empty attributes (and so without DontDelete) */

this.bar = 1;
delete bar; // true
typeof bar; // "undefined"

八、Eval code
  在Eval中建立的變數或方法比較特別,沒有DontDelete特性,也就是說可以刪除。

複製代碼 代碼如下:
eval("var x = 1;");
console.log(x); // 1
delete x;
console.log(typeof x); // undefined

eval("function test(){ var x=1; console.log(delete x);/* false */;return 1;}");
console.log(test()); // 1
delete test;
console.log(typeof test); // undefined  

注意,這裡說的在Eval中建立的變數或方法不包括方法內部的變數或方法,如上面代碼中的紅色部分,仍然跟之前講的一致:不能被刪除。

九、FireBug的困惑

  我們看一段在FireBug中執行的代碼結果:
複製代碼 代碼如下:
var x=1;
delete x;
console.log(typeof x);//undefined

function y(){
var z=1;
console.log(delete z);//false
}
y();
delete y;
console.log(typeof y);//undefined

這明明是違反上述規則的,但跟上面第八點對比後發現,這正在代碼在eval中執行的效果。雖然沒有證實,但我猜測FireBug(Chrome Developer tool)中控制台代碼是用eval執行的。

所以,當大家在測試JS代碼時,如果涉及到當前上下文環境時特別要注意。

十、delete操作符刪除的對象

  C++中也有delete操作符,它刪除的是指標所指向的對象。例如:

複製代碼 代碼如下:
class Object {
public:
Object *x;
}

Object o;
o.x = new Object();
delete o.x; // 上一行new的Object對象將被釋放

但Javascript的delete與C++不同,它不會刪除o.x指向的對象,而是刪除o.x屬性本身。
複製代碼 代碼如下:
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"],兩者效果相同。

十一、其他不能被刪除的屬性

  除了上面說過的內建屬性(即預定義屬性)不能被刪除外,prototype中聲明的屬性也不能delete:
複製代碼 代碼如下:
function C() { this.x = 42; }
C.prototype.x = 12;
C.prototype.y = 13;

var o = new C();
o.x; // 42, 建構函式中定義的o.x

delete o.x; //true 刪除的是自身定義的x
o.x; // 12, prototype中定義的o.x,即使再次執行delete o.x也不會被刪除

delete o.y; //true,因為 o自身沒有o.y屬性,y存在於prototype鏈中,也就是說對象自身屬性和prototype屬性是不同的
o.y; //13


小結

  上面說了那麼多,希望對大家認識JavaScript中的Delete有所協助。由於水平有限,不保證完全正確,如果發現錯誤歡迎指正。

原文為:
  1、http://perfectionkills.com/understanding-delete/(英文)
  2、http://nanto.asablo.jp/blog/2008/01/09/2552470(日文)

本文首發http://jscode.cnblogs.com

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.