摘要:看書時,發現下的控制臺或者里,表現跟理論上不太一樣,表現則很正常,難道有問題搜了篇文章,看完才知道問題出在哪里,分享一下。
看書時,發現firefox 35.0下的控制臺或者firebug里,delete表現跟理論上不太一樣,chrome表現則很正常,難道firefox有問題?
搜了篇文章,看完才知道問題出在哪里,分享一下。Understanding delete
Theory那么, 為什么我們能夠刪除對象的屬性:
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操作符只會返回false
要理解這個, 我們首先需要掌握這些有關變量實例和屬性特性的概念——這些概念很不幸地, 很少在JavaScript書中被提及. 我將試著在接下來的幾個段落中簡單地復習一下這些概念. 這些概念是很難理解的!如果你不在乎"為什么這些東西會以這種方式工作"的話,盡情跳過這一章節好了.
代碼的類型:
在ECMAScript中, 有3種不同類型的可執行代碼: 全局代碼(Global code), 函數代碼(Function code)和 Eval代碼(Eval code). 這些類型從名稱上來說或多或少是有自解釋性的, 這里有一個簡短的概述:
當一段源代碼被看成程序(Program)時, 它將會在全局環境下被執行, 并且被認為是全局代碼(Global code). 在一個瀏覽器環境中, 腳本元素的內容通常被解釋為程序, 因此被作為全局代碼來執行. 任何直接在一個函數中執行的代碼顯然被認為是函數代碼(Function code). 在瀏覽器中, 事件屬性的內容(如執行上下文(Execution context):)通常被解釋成函數代碼. 最后, 被應用到內置函數eval的代碼文本被解釋成Eval代碼(Eval code). 很快我們會發現為什么這種類型是特殊的.
當ECMAScript代碼執行時, 它通常會發生在特定的執行上下文中.執行上下文是一個有些抽象的實體概念, 它能幫助理解范圍(Scope)和變量實例(Variable instantiation)是如何工作的. 對三種可執行代碼的每一種, 都有一個執行上下文相對應. 當一個函數被執行的時候, 我們說"程序控制進入了函數代碼的執行上下文"; 當一段全局代碼被執行時, 程序控制進入了全局代碼的執行上下文, 等等.
正如你所見, 執行上下文可以在邏輯上構成一個堆棧. 首先, 可能有一段全局代碼和其自己的執行上下文, 然后這段代碼可能會調用一個函數, 并帶著它(函數)的執行上下文. 這段函數可以調用另外一個函數, 等等等等. 即使函數是遞歸調用的, 每次調用時被也會進入一個新的執行上下文.
活動對象(Activation object) / 變量對象(Variable Object):
每一個執行上下文都有一個跟其所關聯的所謂變量對象(Variable Object). 類似于執行上下文, 變量對象是一個抽象實體, 一種用來描述變量實例的機制. 有趣之處在于, 在源代碼中聲明的變量和函數通常會被當做屬性(properties)增加到這個變量對象上.
當程序控制進入全局代碼的執行上下文時, 一個全局對象(Global 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
好, 所以全局變量會變成全局對象的屬性, 但是局部變量(那些在函數代碼中定義的變量)會發生什么呢? 其實它們的行為也非常類似: 它們會變成變量對象(Variable object)的屬性. 唯一的不同在于, 當在函數代碼中時, 一個變量對象并不是全局對象, 而是所謂的活動對象(Activation object). 活動對象在會每次進入函數代碼的執行上下文時被創建.
并不是只有在函數代碼中聲明的變量和函數會變成活動對象的屬性; 這也會在每個函數參數(對應相應的形式參數的名稱)和一個特殊的Arguments對象(以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代碼中聲明的變量會成為調用者上下文(calling context)的變量對象的屬性. Eval代碼只是簡單地使用調用它的代碼的執行上下文的變量對象.
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 */ })();屬性的特性(property attributes)
我們幾乎是已經在這了. 既然我們已經很清楚在變量上發生了什么(它們變成了屬性), 唯一剩下的需要理解的概念就是屬性的特性(property attributes)了. 每一個屬性可以擁有0個或多個特性, 它們從以下集合中選取: ReadOnly, DontEnum, DontDelete和 Internal. 你可以把它們認為是flags —— 一種特性可以在屬性中存在, 也可以不存在. 對于我們今天的討論來說, 我們只對DontDelete感興趣.
當被聲明的變量和函數成為變量對象(或者函數代碼的活動對象, 或全局代碼的全局對象)的屬性時, 這些屬性在創建時就帶上了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變量(或者, 正如我們現在所知道的, 一個活動對象的屬性)擁有DontDelete. 函數實例的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");未聲明的賦值:
你可能還記著, 未聲明的賦值會在全局對象上建立一個屬性, 除非這個屬性已經在這個作用域鏈中全局對象之前的其它地方被找到. 并且, 現在我們知道屬性賦值和變量聲明的不同之處——后者會設置DontDelete屬性, 但前者不會. 我們必須清楚, 為什么未聲明的賦值會建立一個可刪除的屬性.
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; delete foo; // false typeof foo; // "number" delete bar; // true typeof bar; // "undefined"
請注意: 特性是在屬性被創建時被決定的, 之后的賦值不會修改已存在屬性的特性. 理解這一點區別非常重要.
/* `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"Firebug的困惑:
在Firebug中發生了什么? 為什么在console中聲明的變量可以被刪除, 這不是違背了我們之前所學到的知識么? 嗯, 就像我之前所說的那樣, Eval代碼在面對變量聲明時會有特殊的表現. 在Eval中聲明的變量實際上是作為不帶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反常行為的依據. 在console中的所有文本都會被當做Eval代碼來解析和執行, 而不是全局或函數代碼. 顯然, 這里聲明的所有變量最后都會成為不帶DontDelete特性的屬性, 所以它們都能被輕松刪除. 我們需要了解這個在全局代碼和Firebug控制臺之間的差異.
通過Eval來刪除變量:
這個有趣的eval行為, 再加上ECMAScript的另一個方面, 可以在技術上允許我們刪除"non-deletable"的屬性. 有關函數聲明的一點是, 它們能夠覆蓋相同執行上下文中同名的變量.
function x(){ } var x; typeof x; // "function"
注意函數聲明是如何獲得優先權并且覆蓋同名變量(或者, 換句話說, 在變量對象中的相同屬性)的. 這是因為函數聲明是在變量聲明之后被實例化的, 并且被允許覆蓋它們(變量聲明). 函數聲明不僅會替換掉一個屬性的值, 它還會替換掉那個屬性的特性. 如果我們通過eval來聲明一個函數, 那個函數就應該會用它自己的特性來替換掉原有的(被替換的)屬性的特性. 并且, 由于通過eval聲明的變量會創建不帶DontDelete特性的屬性, 實例化這個新函數將會實際上從屬性中刪除已存在的DontDelete特性, 從而使得一個屬性能夠被刪除(并且, 顯然會將其值指向新創建的函數).
var x = 1; /* Can"t delete, `x` has DontDelete */ delete x; // false typeof x; // "number" eval("function x(){}"); /* `x` property now references function, and should have no DontDelete */ typeof x; // "function" delete x; // should be `true` typeof x; // should be "undefined"
不幸的是, 這種"欺騙"在目前的任何實現中都不起作用. 也許我在這漏掉了什么, 或者是這種行為只是太晦澀了以至于實現者都沒有注意到它.
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/91451.html
摘要:本文適合適合對繪制圖形學前端可視化感興趣的讀者閱讀。結論已經明顯瀏覽器下,用下繪制繪制圖的時候,的設置將不生效。下面是一段用于測試的代碼,表示用源圖像的形狀去挖空目標圖像。后續繪制用臨時的替代圖片。 本文適合適合對canvas繪制、圖形學、前端可視化感興趣的讀者閱讀。 楔子 所有的事情都會有一個起因。最近產品上需要做一個這樣的功能:給一些圖形進行染色處理。想想這還不是順手拈來的事情,早...
摘要:在各大瀏覽器廠商的發展過程中,它們對的標準各有不同的實現,標準不同存在差異所以產生兼容性的問題。它是一種對特定的瀏覽器或瀏覽器組顯示或隱藏規則或聲明的方法。但是及更低版本瀏覽器會繼續解析。 為什么會存在瀏覽器兼容問題? 首先要了解兼容,我們先得了解一下為什么會存在瀏覽器兼容問題。在各大瀏覽器廠商的發展過程中,它們對web的標準各有不同的實現,標準不同存在差異所以產生兼容性的問題。 瀏覽...
摘要:最近在幫人解決下的一些兼容問題。驗證不通過的話,輸入框會加上紅色的邊框。然后妹紙在描述中說的是瀏覽器中,修改密碼頁面,輸入框中不輸入任何字符,輸入框顏色也是紅的我還以為又是哪里的寫得不對呢。最后發現,輸入框好像都帶了個屬性。 因為工作一年多以來,做的工作基本都是和webkit系列打交道。 先是做m站,后來做了兩個app內嵌的hybrid項目,從來只考慮webkit前綴和相關的偽類。 最...
摘要:最近在幫人解決下的一些兼容問題。驗證不通過的話,輸入框會加上紅色的邊框。然后妹紙在描述中說的是瀏覽器中,修改密碼頁面,輸入框中不輸入任何字符,輸入框顏色也是紅的我還以為又是哪里的寫得不對呢。最后發現,輸入框好像都帶了個屬性。 因為工作一年多以來,做的工作基本都是和webkit系列打交道。 先是做m站,后來做了兩個app內嵌的hybrid項目,從來只考慮webkit前綴和相關的偽類。 最...
摘要:譯者按使用和,可以禁止重寫對象。引入了一些方法,允許開發者限制對象重寫。不要重寫他人的對象不要重寫他人的對象,這是的黃金法則。如果你修改的是對象比如,這會導致非常嚴重的后果。 譯者按: 使用Object.preventExtensions()、Object.seal()和Object.freeze(),可以禁止重寫JavaScript對象。 譯者:Fundebug 原文:Preve...
閱讀 1800·2021-11-24 10:21
閱讀 1212·2021-09-22 15:25
閱讀 3173·2019-08-30 15:55
閱讀 711·2019-08-30 15:54
閱讀 3464·2019-08-30 14:20
閱讀 1662·2019-08-30 14:06
閱讀 643·2019-08-30 13:11
閱讀 3151·2019-08-29 16:43