今天有個朋友跟我說了一下之前發(fā)的文章當(dāng)中統(tǒng)計字符串出現(xiàn)的次數(shù)格郁,for in其實可以用for of代替,因為沒有使用下標(biāo)的必要独悴,剛開始的時候例书,我還真不知道這兩個到底有什么區(qū)別,于是我便決定去研究了一番刻炒。决采。。結(jié)果讓我又漲知識了~
談?wù)刦or in
我們平常遍歷數(shù)組可能最常用的就是for循環(huán)坟奥,其次就是forEach树瞭,map,filter爱谁,some晒喷,every等等方法,只是這些方法的返回值不一樣访敌。forEach更沒有返回值凉敲,使用break不能中斷循環(huán),return也不能返回到外層函數(shù)。其實for in也可以用來遍歷數(shù)組荡陷。
let arr = [1,2,3];
for(let i in arr){
console.log(i, '--------', arr[i]);
}
上面可以打印出arr的每一個元素以及索引:perfect雨效!你是不是以為這樣挺完美的?那么問題來了废赞,如果有人偷偷在Object的原型對象和Array的原型對象上添加一些方法,或者給數(shù)組添加一些其他屬性叮姑,看看會發(fā)生什么情況:
Object.prototype.objMethod = function () {};
Array.prototype.arrMethod = function () {};
let arr = [1,2,3];
arr.attr = 'hi';
for(let i in arr){
console.log(i, '---------', arr[i]);
}
打印結(jié)果如下:
MDN對for in的用法定義為:以任意順序遍歷一個對象的除[Symbol]以外的可枚舉屬性。
我們來簡單抓一下重點朱盐,理解一下這句話:
1.任意順序遍歷
也就是說for in遍歷數(shù)組對象是無法保證將按照數(shù)組的索引順序來訪問數(shù)組的屬性的群嗤。
舉個例子:
let arr = [];
arr[2] = 'c';
arr[1] = 'b';
arr[0] = 'a';
for(let i in arr){
console.log(i, '--------', arr[i]);
}
在IE8及以下版本中,會打印出:'2' -------- 'c', '1' -------- 'b', '0' -------- 'a'兵琳。這是不是很扯淡狂秘。。躯肌。
我再舉個例子:
let arr = [];
arr[5] = 10;
for(let i in arr){
console.log(i, '--------', arr[i]);
}
打印結(jié)果如下:·
2.可枚舉屬性
什么是可枚舉屬性呢?
在js的每個對象的屬性都有一個叫enumerable的屬性嫡丙,(什么拴袭?你問我屬性也有屬性嗎?當(dāng)然啦曙博!萬物皆對象嘛~ 對象的屬性當(dāng)然也是對象啦~)這個屬性的true和false決定了這個屬性是否可以枚舉(就是讓一些方法訪問到這個屬性)拥刻,那么常見的有兩種,第一就是js基本包裝類型自帶的原型屬性不可枚舉羊瘩,如Object泰佳,Array等;第二就是通過Object.defineProperty()方法指定enumeralbe為false的屬性不可枚舉尘吗。
而通過prototype添加的屬性和方法默認是可枚舉的(ES5之前不能定義為不可枚舉)逝她,其實這也就能說得通為什么for in能遍歷到原型對象上的方法,而不能遍歷到Object或Array內(nèi)部的方法了睬捶。
for in由于歷史遺留問題黔宛,它遍歷的實際上是對象的屬性名稱。一個Array數(shù)組實際上也是一個對象擒贸,它的每一個元素索引都被視為一個屬性臀晃。
總結(jié)一下用for in 遍歷數(shù)組的缺點:
1.遍歷順序在某些情況下觉渴,可能不是按照實際數(shù)組的內(nèi)部順序。
2.它會遍歷數(shù)組的所有可枚舉屬性徽惋,包括原型對象上的屬性案淋。
3.遍歷的索引是字符串型的數(shù)字,而不是數(shù)字型的险绘,所以不能夠直接進行幾何運算踢京。(仔細觀察上面for in的打印結(jié)果,i打印出來的顏色是黑色的宦棺,這是字符串型才有的特征瓣距,而數(shù)字型打印出來的顏色應(yīng)該是藍色的。)
看來for in貌似不太適合用來遍歷數(shù)組啊代咸,那怎么解決呢蹈丸?for of就誕生了!
談?wù)刦or of
我們使用for of遍歷數(shù)組則完全避免了上述的一些問題呐芥。
簡單來說逻杖,它可以直接獲取到數(shù)組的值,只會遍歷數(shù)組本身的元素贩耐,即使你在原型對象上添加了方法或自己添加了屬性弧腥。
Object.prototype.objMethod = function () {};
Array.prototype.arrMethod = function () {};
let arr = [1,2,3];
arr.attr = 'hi';
for(let val of arr){
console.log(val);
}
打印結(jié)果如下:你看,被人偷偷在原型對象上加上的方法和給數(shù)組添加的屬性都沒有顯示出來了潮太!這是因為原型鏈上的對象并沒有默認部署遍歷器生成方法管搪。
MDN對for of的用法定義為:在可迭代對象(包括Array,Map,Set,String,TypedArray,arguments對象等等)上創(chuàng)建一個迭代循環(huán),調(diào)用自定義迭代器铡买,并為每一個不同屬性的值執(zhí)行語句
是不是又很抽象更鲁?我們再來解釋一下這句話:
1.可迭代,迭代器
其實奇钞,在js中Array澡为,Map等數(shù)據(jù)結(jié)構(gòu)都存在著一個叫做迭代器(Iterator)的東西,它是一種接口景埃,為各種不同的數(shù)據(jù)結(jié)構(gòu)提供統(tǒng)一的訪問機制媒至,任何數(shù)據(jù)結(jié)構(gòu)只要有這個接口,就可以完成遍歷操作谷徙,這個迭代器的作用有三個:一是為各種數(shù)據(jù)結(jié)構(gòu)拒啰,提供一個統(tǒng)一的、簡便的訪問接口完慧;二是使得數(shù)據(jù)結(jié)構(gòu)的成員能夠按某種次序排列谋旦;三是 ES6 創(chuàng)造了一種新的遍歷命令for...of循環(huán),Iterator 接口主要供for...of消費。
那么如果我們用for of來遍歷對象呢册着?
let obj = {
uname: 'pink',
age: 18,
sex: '男'
}
for (let val of obj) {
console.log(val);
}
打印一下:What?直接報錯了拴孤!這是為啥呢?
其實對象(Object)這種數(shù)據(jù)結(jié)構(gòu)是沒有默認的Iterator接口的甲捏,需要自己部署這個迭代器演熟,才會被for of循環(huán)遍歷。對象(Object)之所以沒有默認部署Iterator接口司顿,是因為對象的哪一個屬性先遍歷绽媒,哪一個屬性后遍歷是不確定的。
總結(jié)
for of適用于遍歷數(shù)組免猾,字符串,map囤热,set等擁有迭代器的對象的集合猎提,但是不能遍歷對象,因為沒有迭代器旁蔼。
for in主要是為遍歷對象而設(shè)計的锨苏,不適用于遍歷數(shù)組。它最常用的地方應(yīng)該是用于調(diào)試棺聊,可以更方便的去檢查對象的屬性伞租。
文中如有錯誤之處歡迎指正~