Array 的方法們 和 object 那些讓人糾結的問題 ……

來源:互聯網
上載者:User

昨天 一個朋友 問了這樣一個問題.  本來類似的代碼測試,以前做過好多... 但發現當我試圖從根源上找出原因的時候,卻有些無所適從. 特記錄下來,待以後求證.


簡單的問題 :var con = function(){};var a = con.prototype = [];var obj = new con;obj.push(1);alert(obj[0]);alert(obj.length); //ie 6 7 8(8怪異模式下)  輸出0 , ie8標準模式 以及 ie9 和其他瀏覽器 輸出1  在ie 下 1 被 push 到哪去了.顯然在push中 瀏覽器都有 this[this.length++] = value 類似的邏輯. (代碼肯定不是這樣, 而維護this.length 的地方 各個瀏覽器可能有區別,這也是ie 6 7 8實現不同的地方之一)另外一個版本 : 接上面obj.push(1);我們再次 obj.push(2);alert(obj[0]);alert(obj[1]); 悲劇出現了.究其原因. 無非是ie6  7 8(怪異模式)沒有正常的維護到 this.length 屬性.讓我們換個思路 :var obj = {length : 0};
[].push.call(obj,1);
[].push.call(obj,2);
alert([obj[0],obj[1],obj.length]);這次所有瀏覽器的行為都正常了.好吧.原來只有obj自身具備.length屬性 而不是原型鏈上繼承來的.貌似ie就ok了. 我們回到初始的版本.並做些修改.再次嘗試下看看.     var con = function(){};
     var a = con.prototype = [];
     var obj = new con;
     obj.length = 0 ;
     //obj.push(1);
     //obj.push(2);
     a.push.call(obj,1);
     a.push.call(obj,2);
     alert(obj[0]);
     alert(obj[1]);ie6 7 8  仍然是老問題.. this.length 沒有被正確維護.  具體原因..我仍然糾結著...  做過幾種嘗試. 比如修改constructor  甚至a.constructor 都 無甚改觀. 唯一我觸碰不到的 是 obj.__proto__了 . 最終留下一個猜測. 當obj 的__proto__指向一個數組的時候.  ie6 7 8反倒腦殘了....接著. 我們可能要嘗試一些其他特別的對象了... 也引發出了另外一些奇怪的問題.  關於 arguments . 首先這次ie系的表現不賴.至少和大多數瀏覽器的行為保持一致... 雖然目前所有我所知道的瀏覽器,沒有一個是嚴格遵守 ecma262 v3 or v5 規範. 在arguments 和 Array原型方法配合演出之前. 我們還要做一些小的demo,來說明一些問題.一個 可能大家都知道 而 NCZ 大神.最近在其blog 提到的有趣問題是 :      function f(a){
          arguments[0] = 10;
          alert(a);
     }
     f();除了chrome 或用了它v8引擎的瀏覽器以外 都列印undefined .原因自然是因為 chrome 對arguments的實現 與其他瀏覽器區別 所導致的...很明顯 chrome 在玩簡單暴利 .  一直關聯.並維護arguments[index]與對應形參的關係... 而其他瀏覽器的問題則是 同樣不考慮 形參,實參數目是否匹配. 一樣把他們關聯起來了.只不過,關聯維護的對象,不是 arguments 而是其可以通過索引訪問的成員們..(可以參考 ref variable) 而且chrome 這裡還有一個 很悲劇的bug ... (chrome15已修複.無論argument或eval,是否存在.下面的代碼中,a都是中是undefined.原因是對es5的遵守.不再無條件相互共用形參和arguments的對應成員的值了)
     Object.prototype['0'] = 'franky'; 
     function f(a){ 

          alert(a); 
          arguments; //如果注釋掉arguments調用.則 alert(a)的結果將完全不同.

        //eval(''); 
     } 
     f();
這裡 eval('') 和 arguments; 都可以導致 列印 franky 而不是 undefined....事實上這個bug 有些類似於 早期的ff (ff3.0之前的版本) spider monkey 引擎 的一個bug .Object.prototype.attr = 'franky';void function(){     var attr = 'ooxx' ;     void function fn(){          alert(attr);// 它十分榮幸的列印了 franky .而不是我們期望的 ooxx.     }();}();see ? 一樣的問題.早期ff 為了遵守ecma標準.(實現“具名”函數運算式 只能在該函數自身範圍內 尋找自身的函數名.)把 一個object 放進 當前範圍鏈對象 的頂端. 而這個object.__proto__ == Object.prototype .. 並忘記了做額外的保險處理. 導致了這個 問題的發生.
回頭看看chrome乾的好事..明顯 eval 和arguments 把其對arguments這個對象的某些保護機制給幹掉了.... 最初,我是這樣想的.但仔細想想 不大對勁... arguments;或 eval('') 是放到alert後面也生效的...那麼問題應該出在詞法分析期.而不是執行期,那麼我們就可以推斷出,chrome 在分析一個函數主體內容相關的時候,會看你是不是有 arguments或eval 如果有 則給這個函數arguments 對象,反之則沒有.這本質上 是一種最佳化. 也就是說chrome程式員,壓根就沒有給arguments任何保護機制.阻止其訪問Object.prototype.
證明這一推論的 論據並不是很充分.但是我還是嘗試給出2個...如果eval 也會觸發這個bug .那麼我們是不是可以考慮為 . eval 具備一個隸屬當前執行內容環境的(作用於當前範圍),獨特的執行內容環境. 他內部一但出現arguments.則就是當前函數的arguments.  所以chrome不得不在 eval出現時, 也派發給當前函數對象 arguments對象.從而出現了這個bug..因為 不僅僅eval,new Fcunction(),window.eval('') 也都具備動態執行語句的特點..而區別則在於 範圍.即 除了 eval(''),其他兩種方法 無法訪問到當前函數的arguments. 
為了鼓勵我們的chrome. 我們接著說其他瀏覽器的 一個有趣的問題 :
function f(a,b,c){ 
          for(var i = arguments.length ; i--; [].shift.call(arguments)); 
          alert(arguments[0]); 
          alert([a,b,c]) ;

f(1,2,5);
這個例子 比較特殊..如果你的瀏覽器足夠多. 你會像我曾經那樣,表情獃滯的一直看著螢幕....好在,我找到了它的答案.ie 8 : 標準模式下 , ie6 , ie7 , ie9, ff , safari , opera9.6     :     undefined     5,5,5 ie 8 : 怪異模式下 ,opera 10     :     5     5,5,5ie 8在怪異模式下完全可以解釋成一種腦殘行為.因為ie 8怪異模式可能考慮要相容早期頁面的一些代碼. 所以此模式下,ie8 把 window.JSON 都給幹掉了. 而一些山寨瀏覽器,當使用核心同為ie8時.直接開啟所謂相容模式.同樣沒有JSON.所以 所有使用ie8核心的山在瀏覽器 跑這個測試的結果 一率會是 5   5,5,5 .除非你有辦法讓他不進入相容模式.但是我所不能理解的是.為什麼ie8 jscript引擎 在進入 所謂相容模式時,會這麼奇怪..它相容的是誰?難道是ie5.5 ? 
再看看opera10 ...這個瀏覽器一直讓人很無語的地方時..很多特性 經常變來變去.時好時壞. 無視之.現在來解釋下 5,5,5出現的原因...shift 方法是這樣工作的 :arguments[0] : 1arguments[1] : 2arguments[2] : 5
第一次shift  它要做的就是 修改[0] 的值,即把 [1]的值給[0] .然後把[2]的值給[1]. 然後 delete arguments[2] . 你可以想象出,3次以後 如果delete 是有效 那麼 [0] [1] [2] 的值都應該是 undefined 因為這三個key 被從arguments object上 移除了.但是 和他們一一關聯的(具備相同堆棧 引用 或 記憶體位址.具體情況視資料類型而定 ) 參數 a , b , c卻把delete 之前的被新賦予的值保留下來了... 自然就都是 5 ...opera10,arguments[0] 沒有被刪除成功.我可以理解為. opera團隊 打算真的遵守 ecma262 v5的規範了...嚴格限制arguments的操作.  至於ie8.... 我仍然無法理解....因為ie9也沒有向標準致敬..何況 是ie8的 所謂相容模式?本來還想 繼續寫  Array 原型上的其他方法 ,諸如 splice  sclie不同瀏覽器區別 以及對應 ie nodeList   和 ff等瀏覽器 .length 唯讀特性 等等等等....    以及 push shift unshift pop 等等 在ie下比較聽話的原因之類的... 突然發現 實在是無體力了... 如果大家有興趣..可以自己試試看. ps : 標題之所以說 object 而不是 arguments 本意也是如此...但是現在看來 有些標題黨了.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.