JS-對象無效屬性與forEach——一個考題引起的思考

NEC前端課的JS考試出成績了臣嚣,趕緊去看了下主觀題錯了哪些,發(fā)現(xiàn)幾個遺漏點:
1.forEach方法的內(nèi)部實現(xiàn)
2.對象的未賦值屬性是否有效

原題

// 以下代碼執(zhí)行完后梯啤,`obj`和`count`的值分別是
var obj = {}, count = 0;
function logArray(value, index, array) {
    count++;
    obj[count] = value;
}
[1, 2, , 4].forEach(logArray);
得分/總分
A.{1: 1, 2: 2, 3: 4}3
B.{}0
C.{1: 1, 2: 2, 3: , 4:4}而线,3
D.{1: 1, 2: 2, 3: , 4:4}4 ×0.00/2.00

當初沒有在console里跑一遍恋日,自認為對數(shù)組還算熟悉膀篮,答案出來后——“始驚次醉終狂”⊙▽⊙夸張了——不過確實刷新了對forEach和對象屬性的認識。

真實的forEach

以前我模擬的forEach實現(xiàn)是醬紫的:

arr.myForEach = function (callback, thisArg) {
    for(var i = 0; i < this.length; i++){
        callback.call(thisArg, this[i], i, this);
    }
}

嗯岂膳,很簡單易懂誓竿,但是沒有一定的錯誤檢測機制。

來看看MDN上的一段polyfill[1]

if (!Array.prototype.forEach) {
    Array.prototype.forEach = function(callback, thisArg) {
        var T, k;
        if (this == null) {//如果調(diào)用forEach方法的對象是null就拋錯
            throw new TypeError(' this is null or not defined');
        }
        var O = Object(this);//獲取調(diào)用forEach的對象
        var len = O.length >>> 0;//手動轉(zhuǎn)為32位整數(shù)
        if(typeof callback !== "function") {//callback檢查
            throw new TypeError(callback + ' is not a function');
        }
        if (arguments.length > 1) {//第二參檢查
            T = thisArg;
        }
        k = 0;
        while (k < len) {
            var kValue;
            if (k in O) {//某個數(shù)組項是否在數(shù)組中
                kValue = O[k];
                callback.call(T, kValue, k, O);
            }
            k++;
        }
    };
}

以上代碼省去了MDN上原有的注釋谈截,另外添加了幾個注釋以跟前面精簡版模擬的forEach對比筷屡。多了幾個關(guān)鍵點:調(diào)用對象的檢查涧偷,數(shù)組長度轉(zhuǎn)32位,callback檢查毙死,第二參檢查燎潮,數(shù)組項有效性檢查。
這里的重點是 數(shù)組項的有效性檢查 扼倘,我們從文章開始給出的原題的運行可以看出forEach對數(shù)組的未賦值項是忽略處理的确封。從MDN上的polyfill看出就是通過if (k in O)來判斷是否忽略的,這個后面會講到唉锌。
因此forEach的實現(xiàn)應(yīng)該至少還要注意忽略無效項隅肥。

對象的無效屬性

前面講到了用if (k in O)來判斷數(shù)組項的有效性,但是其原理是什么呢袄简?
這里用到了in操作符來判斷某個屬性名k是否包含在一個對象O中腥放,為何需要這么判斷呢?
首先我們知道數(shù)組也是對象绿语,數(shù)組中的每個項其實就是這個對象中的某個屬性秃症,只不過屬性名是"0""1"吕粹、"2"...這樣排列的种柑。
然后回頭來看,難道不是所有的數(shù)組項(無論是否已經(jīng)賦值)都自動成為數(shù)組的(有效)屬性/項么匹耕?
我們測試一段代碼

var arr = [1,,2,,3];
arr;//[1, undefined × 1, 2, undefined × 1, 3]
arr[1];//undefined
'0' in arr;//true
'1' in arr;//false
'3' in arr;//false
Object.getOwnPropertyNames(arr);//["0", "2", "4", "length"]

從以上測試代碼可以看出數(shù)組項未賦值或者說值為undefined的聚请,實際上都沒有被算到這個數(shù)組對象的屬性中,只不過是在數(shù)組表現(xiàn)時稳其,有一個“占坑”的跡象——undefined × 1驶赏。
這個undefined × 1表示連續(xù)的值為undefined的數(shù)組項有1個,如果是連續(xù)n個就是× n既鞠,想想ES數(shù)組的自動更新特性煤傍,看看以下代碼:

var arr =[1,2];
arr[10] = 5;
arr;//[1, 2, undefined × 8, 5]

回過頭來,從這里就不難理解為何在forEach中可以通過if (k in O)判斷數(shù)組項的有效性了:in操作符可以判斷某個屬性名是否包含在一個對象或者對象從原型繼承來的屬性名列表中嘱蛋,而屬性值為undefined的屬性名是不會包含在這個屬性名列表中的蚯姆,因此就可以判斷某個數(shù)組項是否未被賦值。從對象的角度來看洒敏,數(shù)組中的未賦值項是不存在數(shù)組對象中的龄恋。

那么是否真是如此呢?反過來想桐玻,是不是一個對象所有值為undefined的屬性就會從對象中“清除”呢篙挽?看看一段測試代碼:

var obj = {a:'tom',b:'jerry'};
obj.a = undefined;
'a' in obj;//true
obj.c = undefined
'c' in obj;//true
Object.getOwnPropertyNames(obj);//["a", "b", "c"]

從上面可以看出,并不是值為undefined的屬性就會被從對象的屬性名列表請清除出去镊靴,它們?nèi)匀皇菍ο蟮膶傩浴?br> 那么為何數(shù)組的就不一樣的呢铣卡?看看這個链韭,仍然是對前面的數(shù)組的例子進行操作

arr[0] = undefined;
'0' in arr;//true
arr.push(undefined);//6
arr[5];//undefined
'5' in arr;//true
arr;//[undefined, undefined × 1, 2, undefined × 1, 3, undefined]

從這里也看到跟上面那段對象的例子相同的結(jié)果,被賦值為undefined的數(shù)組項煮落,也仍然會是數(shù)組對象的屬性敞峭。不過這里有一點很明顯的區(qū)別,undefinedundefined × 1蝉仇,主動添加或者賦值的是沒有后面的× 1的旋讹。

綜合來看

那么碼了這么多字,還是沒搞懂為何要通過if (k in O)來判斷無效屬性轿衔,而值為undefined的屬性也并非就是無效的沉迹?
那么還有種情況,就是未聲明的變量害驹,值為undefined但是一般無法直接打印鞭呕,只能通過typeof操作符來間接了解。也就是說[1,,2,,3]中的第1項和第3項(從第0項開始)都是為未聲明的變量宛官。如果要使得數(shù)組項有效(即也成為數(shù)組對象的屬性)葫松,可以對其進行賦值操作(賦值undefined也可以)。

arr[3] = 123;//123
Object.getOwnPropertyNames(arr);//["0", "2", "3", "4", "length"]

因為對象中的屬性底洗,不能真正的使用var來進行變量聲明腋么,所以都是以所賦的值為判斷標準的。所以你可以看到在對沒有d屬性一個對象obj使用obj.d來進行屬性訪問亥揖,結(jié)果會返回undefined珊擂,因為這個屬性是未聲明的。
因此MDN的polyfill那段代碼要用if (k in O)來判斷數(shù)組中的項是否被賦值(已聲明)過费变,來排除那些“占坑”的數(shù)組項未玻。

看看下面這兩段的輸出差異:

var arr = [1,2,undefined,4];
arr.forEach(function(item,index,array){console.log(index,item);});
var arr = [1,2,,4];
arr.forEach(function(item,index,array){console.log(index,item);});

雖然兩段代碼訪問arr[2]輸出都是undefined,但是本質(zhì)差別正如同上面的分析:前者是賦值為undefined(已聲明)胡控,后者是未聲明的。

bonus

要注意數(shù)組的中括號內(nèi)的最后一個逗號后面如果沒有值旁趟,這個最后項會被忽略昼激,如下:

[1,2,];//[1, 2]
[1,2,].length;//2
[1,2,,];//[1, 2, undefined × 1]
[1,2,,].length;//3
[1,2,,,];//[1, 2, undefined × 2]
[1,2,,,].length;//4

  1. Array.prototype.forEach() ?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市锡搜,隨后出現(xiàn)的幾起案子橙困,更是在濱河造成了極大的恐慌,老刑警劉巖耕餐,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凡傅,死亡現(xiàn)場離奇詭異,居然都是意外死亡肠缔,警方通過查閱死者的電腦和手機夏跷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進店門哼转,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人槽华,你說我怎么就攤上這事壹蔓。” “怎么了猫态?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵佣蓉,是天一觀的道長。 經(jīng)常有香客問我亲雪,道長勇凭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任义辕,我火速辦了婚禮虾标,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘终息。我一直安慰自己夺巩,他們只是感情好,可當我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布周崭。 她就那樣靜靜地躺著柳譬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪续镇。 梳的紋絲不亂的頭發(fā)上美澳,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天,我揣著相機與錄音摸航,去河邊找鬼制跟。 笑死,一個胖子當著我的面吹牛酱虎,可吹牛的內(nèi)容都是我干的雨膨。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼读串,長吁一口氣:“原來是場噩夢啊……” “哼聊记!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起恢暖,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤排监,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后杰捂,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體舆床,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了挨队。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谷暮。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖瞒瘸,靈堂內(nèi)的尸體忽然破棺而出坷备,到底是詐尸還是另有隱情,我是刑警寧澤情臭,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布省撑,位于F島的核電站,受9級特大地震影響俯在,放射性物質(zhì)發(fā)生泄漏竟秫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一跷乐、第九天 我趴在偏房一處隱蔽的房頂上張望肥败。 院中可真熱鬧,春花似錦愕提、人聲如沸馒稍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纽谒。三九已至,卻和暖如春如输,著一層夾襖步出監(jiān)牢的瞬間鼓黔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工不见, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留澳化,地道東北人。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓稳吮,卻偏偏與公主長得像缎谷,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子灶似,可洞房花燭夜當晚...
    茶點故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 第5章 引用類型(返回首頁) 本章內(nèi)容 使用對象 創(chuàng)建并操作數(shù)組 理解基本的JavaScript類型 使用基本類型...
    大學一百閱讀 3,237評論 0 4
  • 第三章 類型慎陵、值和變量 1、存取字符串喻奥、數(shù)字或布爾值的屬性時創(chuàng)建的臨時對象稱做包裝對象,它只是偶爾用來區(qū)分字符串值...
    坤少卡卡閱讀 639評論 0 1
  • 一捏悬、必做作業(yè):修改第一次練習的A1 片段 一: 選自《堅持撞蚕,一種可以養(yǎng)成的習慣》 【R:閱讀原文】 盡量找出不被侵...
    陽光語錄閱讀 248評論 0 0
  • 如何確認一家公司是否合規(guī)? 我想這是我們在考察一個全新的項目時,最重要的一件事情过牙。 沒有人希望奮斗打拼了一段時間后...
    弄月人閱讀 705評論 0 0
  • 2017年7月12日 18:00-20:00 膝旋轉(zhuǎn)與彎曲練習 直角式坐姿甥厦,兩腿向前伸直纺铭。 彎曲右膝,腳掌平貼墊...
    悠悠12321閱讀 389評論 0 0