1. 程式人生 > >深入理解javascript 中的 delete(轉)

深入理解javascript 中的 delete(轉)

在這篇文章中作者從《JavaScript面向物件程式設計指南》一書中關於 delete 的錯誤講起,詳細講述了關於 delete 操作的實現, 侷限以及在不同瀏覽器和外掛(這裡指 firebug)中的表現。

下面翻譯其中的主要部分。

...書中聲稱

“函式就像一個普通的變數那樣——可以拷貝到不同變數,甚至被刪除”

並附上了下面的程式碼片段作為說明:

>>> var sum = function(a, b) {return a+b;};
>>> var add = sum;
>>> delete sum;
true
>>> typeof sum; "undefined"

你能發現片段中的問題嗎? 這個問題就是——刪除 sum 變數的操作不應該成功; delete 的宣告不應該返回 true 而 typeof sum 也不應該返回為 undefined。 因為,javascript 中不能夠刪除變數,至少不能以這個方式宣告刪除。

那麼這個例子發生了什麼? 是列印錯誤或者玩笑? 應該不是。 這個片段是 firebug 控制檯中的一個實際輸出,而 Stoyan(上面所說書的作者)應該正是用它做的快速測試。 這彷彿說明了 firebug 有一些不同的刪除規則。 正是 firebug 誤導了 Stoyan! 那麼這裡面究竟是怎麼回事呢?

為了回答這個問題,我們需要了解 delete 運算子在 Javascript 中是如何工作的: 哪些可以被刪除,哪些不能刪除以及為什麼。 下面我試著解釋一下這方面的細節。 我們將通過觀察 firebug 的“奇怪”的表現而認識到它實際上完全不“奇怪”; 我們將深入瞭解那些,當我們宣告變數、函式,賦值屬性和刪除它們時的,隱藏在背後的細節; 我們將看一下瀏覽器對此的實現和一些有名的 bug; 我們還會討論到 ECMAScript 版本 5 中的嚴格模式(strict mode)以及它如何改變 delete 運算子的行為。

我在下面交替使用的 Javascript 和 ECMPScript 一般都指 ECMAScript(除非當明確談到 Mozilla 的 JavaScript™ 實現時)。

意料之中的,網路上目前對於 delete 的解釋非常少(筆者按:這篇文章寫於 2010 年 1 月)。 MDC(MDN]) 的資源大概是這其中最詳細的了,但不幸的是它遺漏了一些有趣的細節,這些細節中就包括了上述 firebug 的奇怪表現。MSDN 文件幾乎沒什麼用處。

一、理論 | Theory

那麼,為什麼我們能刪除一個物件的屬性:

var x = { a: 1 };
delete x.a; // true
x.a; // undefined

但卻不能刪除一個變數:

var x = 1;
delete x; // false;
x; // 1

也不能刪除一個函式:

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

注意:delete 只有當一個屬性無法被刪除時才返回 false。

為了理解這一點,我們需要首先把握一些概念: 變數例項化(variable instantiation)和屬性的內部屬性(property attributes) (譯者按:關於 property 和 attributes 的區別見參考文章,根據下面涉及到的內容,擬譯成內部屬性) ——這些很少在 javascript 書中被提到。 在下面幾段中我將試著簡短地回顧這些內容,要理解它們並不難。 如果你並不關注它們表現背後的原因,可以跳過這一章。

1.1、程式碼的型別 | Type of code

ECMAScript 中有三類可執行程式碼:

  1. 全域性程式碼 Global code
  2. 函式程式碼 Function code
  3. Eval code

這幾類的含義大致就像它們命名的那樣,但還是快速地回顧一下:

  1. 當一個原始檔被看做是一個程式,它在全域性作用域(scope)內執行,而這就被認為是一段全域性程式碼 Global code。 在瀏覽器環境下,SCRIPT 元素的內容通常都被解析為一個程式,因而作為全域性程式碼來執行。

  2. 當然,任何在一段函式中直接執行的程式碼就被認為是一段函式程式碼 Function code, 在瀏覽器環境下,事件屬性的內容(e.g. <a onclick="...")通常都作為函式程式碼來解析和執行。

  3. 最後,放入內建函式 eval 中的程式碼就作為 Eval code 來解析。 我們將很快看到為什麼這一型別是特殊的。

1.2、程式碼執行的上下文 | Execution Context

當 ECMAScript 程式碼執行時,它總是發生在一個確定的執行上下文(context)中。 執行作用域是一個抽象實體,它有助於理解作用域和變數例項化的工作原理。 上面三類可執行程式碼都有各自的執行上下文。 當函式程式碼執行時,我們說控制端進入了函式程式碼的執行上下文; 當全域性程式碼執行時,我們說控制端進入了全域性程式碼的執行上下文,以此類推。

正如你所見,執行上下文在邏輯上是一個棧(stack)。 首先可能有一段全域性程式碼,它擁有屬於自己的執行上下文; 在這段程式碼中可能呼叫一個函式,這個函式同樣擁有屬於自己的執行上下文; 這個函式可能呼叫另一個函式,等等。 即使當函式遞迴呼叫自己時,在每一步呼叫中仍然進入了不同的執行上下文。

1.3、活化物件和變數物件 | Activation object / Variable object

每一個執行上下文都有一個與之相關聯的變數物件(Variable object)。 和它相似的,變數物件也是一個抽象實體,一種用來描述變數例項化的機制。 而有趣的是,在一段原始碼中宣告的變數和函式事實上被作為變數物件(Variable object)的屬性(properties)而新增到變數物件中

當控制進入了全域性程式碼的執行上下文時,一個全域性物件被用作變數物件。 這恰恰是為什麼全域性宣告的變數和函式變成一個全域性物件的屬性的原因:

var GLOBAL_OBJECT = this;
var foo = 1;
GLOBAL_OBJECT.foo; // 1
function bar() {};
typeof GLOBAL_OBJECT.bar; // "function"
GLOBAL_OBJECT.bar === bar; // true

Ok, 所以全域性變數成了全域性函式的屬性,那麼區域性變數——那些在函式程式碼(Function code)中宣告的變數呢? 事實上那很簡單:他們也成了變數物件的屬性。 唯一的區別是,在函式程式碼中,變數物件不是一個全域性物件, 而是一個我們稱之為活化物件(Activation object)。 每次進入函式程式碼的執行上下文時都會建立一個活化物件。

並非只有在函式程式碼中宣告的變數和函式才成為活化物件的屬性: 函式的每一個實參(arguments,以各自相對應的形參的名字為屬性名), 以及一個特殊的Arguments物件(以arguments為屬性名)同樣成為了活化物件的屬性。 需要注意的是,活化物件作為一個內部的機制事實上不能被程式程式碼所訪問。

(function(foo) {
    var bar = 2;
    function baz() {};
    /*
        在抽象的過程中,
        特殊的'arguments'物件變成了所在函式的活化物件的屬性:
        ACTIVATION_OBJECT.arguments = arguments;
        ...引數'foo‘也是一樣:
        ACTIVATION_OBJECT.foo; // 1
        ...變數'bar'也是一樣:
        ACTIVATION_OBJECT.bar; // 2
        ...函式'baz'也是一樣:
        typeof ACTIVATION_OBJECT.baz; // "function"
      */
}) (1);

最後,Eval code 中宣告的變數成為了上下文的變數物件(context's Variable object)的屬性。 Eval code 簡單地使用在它呼叫中的執行上下文的變數物件。

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

(function() {
    eval('var bar = 2');

    /*
        在抽象過程中
        ACTIVATION_OBJECT.bar; // 2
    */
}) ();

1.4、屬性的內部屬性 | Property attributes

就要接近主題了。 現在我們明確了變數發生了什麼(它們成了屬性),剩下的需要理解的概念就是屬性的內部屬性(property attributes)。 每一個屬性擁有零至多個如內部屬性——*ReadOnly,DontEnum,DontDelete和Internal**。 你可以把它們想象為標籤——一個屬性可能擁有也可能沒有某個特殊的內部屬性。 在今天的討論中,我們所感興趣的是 DontDelete。

當宣告變數和函式時,它們成為了變數物件(Variable object)——要麼是活化物件(在函式程式碼中), 要麼是全域性物件(在全域性程式碼中)——的屬性,這些屬性伴隨生成了內部屬性 DontDelete。 然而,任何顯式/隱式賦值的屬性不生成 DontDelete。 而這就是本質上為什麼我們能刪除一些屬性而不能刪除其他的原因。

var GLOBAL_OBJECT = this;

/* 'foo'是全域性物件的一個屬性,
    它通過變數宣告而生成,因此擁有內部屬性DontDelete
    這就是為什麼它不能被刪除*/
var foo = 1;
delete foo; // false
typeof foo; // "number"

/* 'bar'是全域性物件的一個屬性,
    它通過變數宣告而生成,因此擁有DontDelete子
    這就是為什麼它同樣不能被刪除*/
function bar() {};
delete bar; // false
typeof bar; // "function"

/* 'baz'也是全域性物件的一個屬性,
    然而,它通過屬性賦值而生成,因此沒有DontDelete
    這就是為什麼它可以被刪除*/
GLOBAL_OBJECT.baz = "baz";
delete GLOBAL_OBJECT.baz; // true
typeof GLOBAL_OBJECT.baz; // "undefined"

1.5、內建和DontDelete | Build-ins and DontDelete

所以這就是所有這一切發生的原因:屬性的一個特殊的內部屬性控制著該屬性是否可以被刪除。 注意:內建物件的一些屬性擁有內部屬性 DontDelete,因此不能被刪除; 特殊的 arguments 變數(如我們所知的,活化物件的屬性)擁有 DontDelete; 任何函式例項的 length (返回形參長度)屬性也擁有 DontDelete:

(function() {
    //不能刪除'arguments',因為有DontDelete
    delete arguments; // false;
    typeof arguments; // "object"

    //也不能刪除函式的length,因為有DontDelete
    function f() {};
    delete f.length; // false;
    typeof f.length; // "number"
}) ();

與函式 arguments 相關聯的屬性也擁有 DontDelete,同樣不能被刪除

(function(foo,bar) {
    delete foo; // false
    foo; // 1

    delete bar; // false
    bar; // "bah"
}) (1,"bah");

1.6、未宣告的變數賦值 | Undeclared assignments

你可能記得,未宣告的變數賦值會成為全域性物件的屬性,除非這一屬性在作用域鏈內的其他地方被找到。 而現在我們瞭解了屬性賦值和變數宣告的區別——後者生成 DontDelete 而前者不生成——這也就是為什麼未宣告的變數賦值可以被刪除的原因了。

var GLOBAL_OBJECT = this;

/* 通過變數宣告生成全域性物件的屬性,擁有DontDelete */
var foo = 1;

/* 通過未宣告的變數賦值生成全域性物件的屬性,沒有DontDelete */
bar = 2;

delete foo; // false
delete bar; // true

注意:內部屬性是在屬性生成時確定的,之後的賦值過程不會改變已有的屬性的內部屬性。 理解這一區別是重要的。

/* 'foo'建立的同時生成DontDelete */
function foo() {};

/* 之後的賦值過程不改變已有屬性的內部屬性,DontDelete仍然存在 */
foo = 1;
delete foo; // false;
typeof foo; // "number"

/* 但賦值一個不存在的屬性時,建立了一個沒有內部屬性的屬性,因此沒有DontDelete */
this.bar = 1;
delete bar; // true;
typeof bar; // "undefined"

二、Firebug 的混亂 | Firebug confusion

那麼, firebug 中發生了什麼? 為什麼在控制檯中宣告的變數能夠被刪除,而不是想我們之前討論的那樣? 我之前說過,Eval code 在它處理變數宣告時有一個特殊的行為: 在 Eval code 中宣告的變數事實上生成一個沒有 DontDelete 的屬性

eval('var foo = 1;');
foo; // 1
delete foo; // true
typeof foo; // "undefined"

在函式程式碼中也是一樣:

(function() {
    eval('var foo = 1;');
    foo; // 1
    delete foo; // true
    typeof foo; // "undefined"
}) ();

而這就是 Firebug 中異常行為的原因了。 所有在控制檯中的除錯文字似乎是以 Eval code 來編譯和執行的,而不是在全域性或函式程式碼中執行。 顯然,其中的變數宣告最終都生成了不帶 DontDelete 的屬性,所以可以被刪除。 所以要小心普通的全域性程式碼和 Firebug 控制檯中程式碼的區別。

2.1、通過eval刪除變數 | Delete variables via eval

這個有趣的 eval 行為,結合 ECMAScript 的另一個方面可以在技術上允許我們刪除那些原本不能刪除的屬性。 這個方面是關於函式宣告——在相同的執行上下文中它們能覆蓋同名的變數:

function x() { };
var x;
typeof x; // “function”

那麼為什麼函式宣告擁有優先權而能覆蓋同名變數(或者換句話說,變數物件(Variable object)的相同屬性)呢? 這是因為函式宣告的例項化過程在變數宣告之後,因此可以覆蓋它們。

(譯者按:函式宣告只能覆蓋宣告而未賦值的同名變數,如果在宣告時賦值了值(e.g. var x = 1)則賦值值的過程在函式初始化之後,函式宣告反而被變數賦值所覆蓋,如下:)

var x = 1;
function x() { };
typeof x; // "number"

函式宣告不止替換了屬性的值,同時也替換了它的內部屬性。 如果我們通過 eval 來宣告函式,這一函式也將用它自己的內部屬性來替換之前的。 而由於在 eval 中宣告的變數生成的屬性沒有 DontDelete, 例項化這個函式將在“理論上”移除原屬性已有的 DontDelete 內部屬性, 而使得這一屬性可以刪除(當然,同時也將值指向了新生成的函式)。

var x = 1;
/*不能刪除,‘x’擁有DontDelete*/
delete x; // false
typeof x; // "number"

eval('function x() { }');
/* 屬性'x'現在指向函式,並且應該沒有DontDelete */
typeof x; // "function"
delete x; // 應該是‘true’;
typeof x; // 應該是"undefined"

不幸的是,這種欺騙技術在我嘗試的各個瀏覽器中都沒有成功。 這裡我可能錯過了什麼,或者這個行為太隱蔽而以至於各個瀏覽器沒有注意到它。

(譯者按:這裡的問題可能在於:函式宣告和變數宣告之間的覆蓋只是值指向的改變, 而內部屬性 DontDelete 則在最初宣告處確定而不再改變,而 eval 中宣告的變數和函式,也只是在其外部上下文中未宣告過的那部分才能被刪除。 關於執行順序,由於 eval 作為函式,它的呼叫永遠在其外部上下文中其他變數和函式宣告之後, 因此相關的內部屬性也已確定,覆蓋的只是值的指向。如下:)

/*  第一個 alert 返回 “undefined”,因為賦值過程在宣告過程和eval執行過程之後;
    第二個alert返回 “false”, 因為儘管x宣告的位置在eval之後,
    但是eval的執行卻在變數宣告之後,因此已無法刪除 */
eval(' alert( x ); alert(delete x) ');
var x = 1;

三、瀏覽器的遵守情況 | Browsers compliance

瞭解事物的工作原理是重要的,但實際的實現情況更重要。 瀏覽器在建立和刪除變數/屬性時都遵守這些標準嗎? 對於大部分來說,是的。

我寫了一個簡單的測試單元來檢查全域性程式碼、函式程式碼和Eval程式碼的遵守情況。 測試單元同時檢測了 delete 操作的返回值和屬性是否像預期那樣被刪除。 delete 的返回值並不像它的實際結果那樣重要,delete 操作返回 true 或 false 並不重要, 重要的是擁有/沒有 DontDelete 的屬性是否被刪除。

現代瀏覽器總的來說還是遵守刪除規則的,以下瀏覽器全部通過測試: Opera 7.54+, Firefox 1.0+, Safari 3.1.2+, Chrome 4+。

Safari 2.x 和 3.0.4 在刪除函式 arguments 時存在問題,似乎這些屬性在建立時不帶 DontDelete,因此可以被刪除。 Safari 2.x 還有其他問題——刪除無引用時(例如delete 1)丟擲錯誤(譯者按:IE 同樣有); 函式宣告生成了可刪除的屬性(奇怪的是變數宣告則正常); eval 中的變數宣告變成不可刪除(而 eval 中的函式宣告則正常)。

與 Safari 類似,Konqueror(3.5,而非4.3)在 delete 無引用和刪除 arguments 是也存在同樣問題。

3.1、Gecko DontDelete bug

Gecko 1.8.x 瀏覽器—— Firefox 2.x, Camino 1.x, Seamonkey 1.x, etc. ——存在一個有趣的 bug:顯式賦值值給一個屬效能移除它的 DontDelete,即使該屬性通過變數或函式宣告而生成。

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

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

令人驚訝的是,IE5.5-8 也通過了絕大部分測試,除了刪除非引用丟擲錯誤(e.g. delete 1,就像舊的 Safari)。 但是,雖然不能馬上發現,事實上 IE 存在更嚴重的 bug,這些 bug 是關於全域性物件。

四、IE bugs

在 IE 中(至少在 IE6-8 中),下面的表示式丟擲異常(在全域性程式碼中):

this.x = 1;
delete x; // TypeError: Object doesn't support this action

而下面則是另一個:

var x =1;
delete this.x; // TypeError: Cannot delete 'this.x'
// 譯者按:在IE8下丟擲此異常,在IE6,7下丟擲的是和上面一樣的異常

這似乎說明,在 IE 中在全域性程式碼中的變數宣告並沒有生成全域性物件的同名屬性。 通過賦值建立的屬性(this.x = 1)然後通過 delete x 刪除時丟擲異常; 通過變數宣告(var x = 1)建立的屬性然後通過 delete this.x 刪除時丟擲另一個(譯者按:在 IE6,7 下錯誤資訊與上面的相同)。

但不只是這樣,事實上通過顯式賦值建立的屬性在刪除時總是丟擲異常。 這不只是一個錯誤,而是建立的屬性看上去擁有了 DontDelete 內部屬性,而按規則應該是沒有的:

this.x = 1;
delete this.x; // TypeError: Object doesn't support this action
delete x; // TypeError: Object doesn't support this action

另一方面,未宣告的變數賦值(那些同樣生成全域性物件的屬性)又確實在IE下能夠正常刪除:

x = 1;
delete x; // true

但如果你試圖通過 this 關鍵字來進行刪除(delete this.x),那麼上面的異常又將丟擲:

x = 1;
delete this.x; //TypeError: Cannot delete 'this.x'

如果歸納一下,我們將發現在全域性程式碼中‘delete this.x’永遠不會成功。 當通過顯式賦值來生成屬性(this.x = 1)時丟擲一個異常; 當通過宣告/非宣告變數的方式(var x = 1 or x = 1)生成屬性時丟擲另一個異常。 而另一方面,delete x 只有在顯示賦值生成屬性(this.x = 1)時才丟擲異常。

在 9 月我討論了這個問題,其中 Garrett Smith 認為在 IE 中全域性變數物件(Global variable object)實現為一個 JScript 物件,而全域性物件則由宿主物件實現。

我們能通過幾個測試來在某種程度上確認這一理論。 注意,this 和 window 似乎引用同一個物件(如果 ‘===’運算子可以信任的話), 而變數物件 Variable object (函式宣告的基礎)則與 this 引用的不同。

function getBase() { return this; };

getBase() === this.getBase(); // false
this.getBase() === this.getBase(); // true
window.getBase() === this.getBase(); // true
window.getBase() === getBase(); // false

五、誤解 | Misconceptions

我們不能低估理解事物工作原理的重要性。 我看過網路上一些關於 delete 操作的誤解。 例如,Stackoverflow 上的一個答案(而且等級還很高),裡面解釋說“delete is supposed to be no-op when target isn’t an object property”。 現在我們瞭解了 delete 操作的核心,也就清楚了這個答案是不正確的。 delete 不區分變數和屬性(事實上在 delete 操作中這些都是引用),而只關心 DontDelete(以及屬性是否已經存在)。

六、'delete'和宿主物件 | ’delete‘ and host object

一個 delete 的演算法大致像這樣:

1. 如果運算元(operand)不是引用,返回 true
2. 如果物件沒有同名的**直接屬性**,返回 true (如我們所知,物件可以是全域性物件也可以是活化物件)
3. 如果屬性已經存在但有 DontDelete,返回 false
4. 否則,刪除移除屬性並返回 true

然而,對於宿主物件(host object)的 delete 操作的行為卻可能是不可預料的。 而事實上這並沒有錯:宿主物件(通過一定規則)允許實現任何操作, 例如讀(內部[[Get]]方法)、寫(內部[[Write]]方法)、刪除(內部[[Delete]]方法),等等。 這種允許自定義[[Delete]]行為導致了宿主物件的混亂。

我們已經看到了在IE中的一些問題:當刪除某些物件(那些實現為了宿主物件)屬性時丟擲異常。 一些版本的 firefox 當試圖刪除 window.location 時丟擲異常(譯者按:IE 同樣丟擲)。 同樣,在一些宿主物件中你也不能相信 delete 的返回值, 例如下面發生在 firefox 中的(譯者按:chrome 中同樣結果;IE 中丟擲異常;opera 和 safari 允許刪除,並且刪除後無法呼叫,姑且算’正常‘,儘管,從下面的討論來看似乎卻是不正常的,它們事實上刪除了不能刪除的屬性,而前面的瀏覽器沒有):

/* 'alert'是’window‘的一個直接屬性(如果我們能夠相信'hasOwnProperty') */
window.hasOwnProperty('alert'); // true

delete window.alert; // true
typeof window.alert; // "function"

delete window.alert 返回 true,儘管這個屬性沒有任何條件可能產生這個結果(按照上面的演算法): 它解析為一個引用,因此不能在第一步返回 true; 它是 window 物件的直接屬性,因此不能在第二步返回 true; 唯一能返回 true 的是當演算法達到最後一步同時確實刪除這個屬性,而事實上它並沒有被刪除。 (譯者按:不,在 opera 和 safari 中確實被刪除了...)。

所以這個故事告訴我們永遠不要相信宿主物件

七、ES5 嚴格模式 | ES5 strict mode

那麼 ECMAScript 第 5 版中的嚴格模式將帶來什麼? 目前介紹了其中的一些限制。 當刪除操作指向一個變數/函式引數/函式宣告的直接引用時丟擲 SyntaxError。 此外,如果屬性擁有內部屬性[[Configurable]] == false,將丟擲 TypeError:

(function(foo) {
    "use strict"; //在函式中開啟嚴格模式

    var bar;
    function baz;
    delete foo; // SyntaxError,當刪除函式引數時
    delete bar; // SyntaxError,當刪除變數時
    delete baz; // SyntaxError,當刪除由函式宣告建立的變數時

    /* function例項的length擁有[[Configurable]] : false */
    delete (function() {}).length; // TypeError
}) ();

而且,在嚴格模式下,刪除未宣告的變數(換句話說,未解析的引用),同樣丟擲 SyntaxError; 與它類似的,相同模式下未宣告的賦值也將丟擲異常(ReferenceError)

"use strict";
delete i_dont_exist; // SyntaxError

i_dont_exist_either = 1; // ReferenceError

看了之前給出的變數、函式宣告和引數的例子,相信現在你也理解了,所有這些限制都是有其意義的。 嚴格模式採取了更積極的和描述性的措施,而不只是忽略這些問題。

八、總結 | Summary

由於這篇文章已經很長了,因此我就不再討論另一些內容(e.g.通過 delete 刪除陣列項及其影響)。 你可以翻閱MDC/MDN 上的文章或閱讀規範然後自己測試。

下面是關於 Javascript 中 delete 如何工作的一個簡單的總結:

  • 變數和函式宣告都是活化(Activation)全域性(Global)物件的屬性。
  • 屬性擁有內部屬性,其中一個—— DontDelete 負責確定一個屬性是否能夠被刪除。
  • 全域性程式碼或函式程式碼中的變數、函式宣告都生成擁有 DontDelete 的屬性。
  • 函式引數同樣是活化物件的屬性,也擁有 DontDelete。
  • Eval程式碼中的變數和函式宣告都生成沒有 DontDelete 的屬性。
  • 新的未宣告的屬性在生成時帶空的內部屬性,因此也沒有 DontDelete。
  • 宿主物件允許以任何它們期望的方式來響應刪除過程。

原文連結 :http://justjavac.com/javascript/2013/04/04/understanding-delete-in-javascript.html

相關推薦

深入理解javascript delete()

在這篇文章中作者從《JavaScript面向物件程式設計指南》一書中關於 delete 的錯誤講起,詳細講述了關於 delete 操作的實現, 侷限以及在不同瀏覽器和外掛(這裡指 firebug)中的表現。 下面翻譯其中的主要部分。 ...書中聲稱 “函式就像一個普通的變數那樣——可以拷貝到不同變數

[]深入理解javascript的立即執行函式(function(){…})()

( function(){…} )()和( function (){…} () )是兩種javascript立即執行函式的常見寫法,最初我以為是一個括號包裹匿名函式,再在後面加個括號呼叫函式,最後達到函式定義後立即執行的目的,後來發現加括號的原因並非如此。要理解

深入理解javascript的立即執行函數(function(){…})()

但是 不可 模仿 不同之處 調用函數 title 指定 作用 () javascript和其他編程語言相比比較隨意,所以javascript代碼中充滿各種奇葩的寫法,有時霧裏看花,當然,能理解各型各色的寫法也是對javascript語言特性更進一步的深入理解。 ( fun

通過例子深入理解javascript的new操作符

not 而是 efi undefine new blog div 函數功能 成功 1.首先看一道題目 1 function Cat(name,age){ 2 this.name=name; 3 this.age=age; 4 } 5 console.l

深入理解JavaScript的函數操作

要求 使用情況 並不是 回文 工作 alert http load 函數 匿名函數 對於什麽是匿名函數,這裏就不做過多介紹了。我們需要知道的是,對於JavaScript而言,匿名函數是一個很重要且具有邏輯性的特性。通常,匿名函數的使用情況是:創建一個供以後使用的函數。 簡單

圖說js的this——深入理解javascriptthis指針

前端 javascript this沒搞錯吧!js寫了那麽多年,this還是會搞錯!沒搞錯,javascript就是回搞錯!…………在寫java的時候,this用錯了,idea都會直接報錯!比如……但是,js,……idea,愛莫能助了……在面向對象編程裏有兩個重要的概念:一個是類,一個是實例化的對象,類是一個

JavaScript進階】深入理解JavaScriptES6的Promise的作用並實現一個自己的Promise

  1.Promise的基本使用 1 // 需求分析: 封裝一個方法用於讀取檔案路徑,返回檔案內容 2 3 const fs = require('fs'); 4 const path = require('path'); 5 6 7 /** 8 * 把一個回

深入理解Javascript的valueOf與toString

基本上,javascript中所有資料型別都擁有valueOf和toString這兩個方法,null除外。它們倆解決javascript值運算與顯示的問題,本文將詳細介紹,有需要的朋友可以參考下。 toString() toString()函式的作用是返回object的字串表示,JavaScript中ob

深入理解Javascript的堆與棧、淺拷貝與深拷貝

Javascript中的淺拷貝與深拷貝 先從JavaScript的資料型別存放的位置堆疊開始說吧 什麼是堆疊? 我們知道計算機領域中堆疊是兩種資料結構,它們只能再一端(稱為棧頂(top))對資料項進行插入和刪除。 堆:佇列優先,先進先出,由作業系統自動分配釋放,存放函式的引數值,區域性變數的

深入理解 JavaScript的變數、值、傳參

1. demo 如果你對下面的程式碼沒有任何疑問就能自信的回答出輸出的內容,那麼本篇文章就不值得你浪費時間了。 var var1 = 1 var var2 = true var var3 = [1,2,3] var var4 = var3 function test (var1, var3) {

深入理解javascript的立即執行函式(function(){…})()

javascript和其他程式語言相比比較隨意,所以javascript程式碼中充滿各種奇葩的寫法,有時霧裡看花,當然,能理解各型各色的寫法也是對javascript語言特性更進一步的深入理解。 ( function(){…} )()和( function (){…}

深入理解Javascript的隱式呼叫

本篇文章給大家詳細介紹了javascript中的隱式呼叫相關知識點,寫的十分的全面細緻,具有一定的參考價值,對此有需要的朋友可以參考學習下。如有不足之處,歡迎批評指正。 不知道用隱式呼叫來形容是否確切,其行為總是隱藏在背後,時不時出來露臉一下,作用貌似不大,但是瞭解一下還是有用

深入理解JavaScript變數作用域

理解JavaScript變數作用域: ------------------ 變數作用域又叫做變數的可見性。在JavaScript中,變數的作用域是由函式限定的,它們要麼是全域性的,要麼是區域性的。·顧

深入理解javascript函式的prototype屬性(原型)

函式的prototype屬性 一、 只要我們建立一個函式,該函式就會自動獲得一個prototype屬性,這個屬性指向函式的原型物件。 建立fn函式自動獲得prototype屬性,該屬性是一個物件即該函式的原型物件,我們可以看到原型物件預設會有一個cons

深入理解JavaScript的for迴圈

簡單for迴圈for-inforEach for-of 下面我們就來看看這 4 種 for 迴圈。 簡單 for 迴圈 下面先來看看大家最常見的一種寫法: var arr = [1, 2, 3]; for(var i = 0; i < arr.length; i

深入理解 JavaScript 的函式

本文旨在提供web開發人員必須瞭解的所有JavaScript函式的基本知識。 函式於軟體開發者而言並不是什麼奇幻世界。如果你的日常活動涉及到編碼,哪怕是一點點,那麼在一天結束的時候,你一定建立/修改了一個或多個函式。 簡而言之函式只不過是一組執行某個操作的語句。函式可

深入理解JavaScript的立即執行函式(function(){……})()

前言:這篇文章主要介紹了深入理解javascript中的立即執行函式,立即執行函式也叫立即呼叫函式,通常它的寫法是用(function(){…})()包住業務程式碼,使用jquery時比較常見,需要的朋友可以參考下 立即執行函式表示式IIFE 作用:IIFE最常用的功

理解javascript的回調函數(callback)【

自己實現 需要 his tab 定義函數 copy 輸入 mil 幹什麽 在JavaScrip中,function是內置的類對象,也就是說它是一種類型的對象,可以和其它String、Array、Number、Object類的對象一樣用於內置對象的管理。因為function

深入理解javascript原型和閉包(

原型鏈 面向 type www. 作用域 url tle das 經歷 深入理解javascript原型和閉包(完結) 說明:   該教程繞開了javascript的一些基本的語法知識,直接講解javascript中最難理解的兩個部分,也是和其他主流面向對

深入理解JavaScript的閉包特性如何給循環的對象添加事件

彈出 所有 了解 ext catch 形參 efi 運行期 -- 初學者經常碰到的,即獲取HTML元素集合,循環給元素添加事件。在事件響應函數中(event handler)獲取對應的索引。但每次獲取的都是最後一次循環的索引。原因是初學者並未理解JavaScri