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