本章簡介
本章內容比較少,有三個分享的知識。你可能都看過了,因為網上也有很多提問和解答,如果沒看過或者沒搞懂,你可以再看看這篇文章。
1. 數組去重方法的演變 -- 走向代碼縮短化
2. [] + {} 和 {} + []的返回結果 -- js的代碼規范及隱士類型轉換
3. ++ [[]][+[]] 的運算 -- 還是++問題
講解部分
數組去重方法的演變
1. es5以前:
es5以前我們數組去重的方法顯得有點笨笨的,我的需要創建一個新數組和一個緩存對象,遍歷原來數組,一個個的push到新數組中,如果新數組有當前的元素,就不加了。
var testArray = [1,1,5,76,9,3,2,7,99,3,'a','a'] var resultArray = null function uniqueArray (arr) { var newArray = [] var cache = {} var item; for (var i = 0; i < arr.length; i++) { item = arr[i] if (!cache[item] ) { // 如果緩存中沒有記錄當前元素,意味着新數組中沒有這個元素,可以添加 cache[item] = 1 newArray.push(item) } } return newArray; } // 毫無疑問,去重了,這里就不展示了 console.log(uniqueArray(testArray))
2. es5時代
試問,es5時代來了,你還在用這樣的方法么,答案肯定是不用了,我們該怎么辦呢,用filter方法
resultArray = testArray.filter(function (item, index, arr) { return index === arr.indexOf(item) }) // 成功 console.log(resultArray)
filter方法是es5中對數組新添加的方法,此方法用來過濾return 為 true的。回調函數接收3個參數,分別是當前元素、當前下標、當前數組(相當於this),代碼中我們用arr.indecOf這個es5的方法來取到當前元素的下標再與index比較,達到了過濾重復元素的效果,看以下簡單過程
var arr = [1,2,1] var arr2 = arr.filter(function (item, index, arr) { return index === arr.indexOf(item) }} // 以下 = 並未賦值,是數學上的等於意思 // 第一個元素 1: item = 1, index = 0, arr.indexOf(1) = 0,成功加入新數組 // 第二個元素 2: item = 2, index = 1, arr.indexOf(2) = 1, 成功加入新數組 // 第三個元素 1: item = 1, index = 2 , arr.indexOf(1) = 0 ,這里發現indexOf得到的下標並不是2,因為indexOf查找到匹配元素就返回,所以這個元素在之前出現過,因此被屏蔽了。 //最終arr2 = [1,2]
3. es6的問世
es6的出現帶來了許多方便的方法,博主也沒有仔細看es6的文檔,這里介紹兩個演變過程。
1). 箭頭函數
resultArray = testArray.filter((item, index, arr) => index === arr.indexOf(item)) // 成功 console.log(resultArray)
箭頭函數在一定程度上減少了我們的代碼量,因此我們可以更簡潔的完成es5方法的代碼。但這並不是最優的
2). Set結構
Set結構可以接收一個數組,然后把它變成Set表,它會自動屏蔽數組中重復的元素,實現自動過濾
resultArray = [...new Set(testArray)] // ok console.log(resultArray)
我們把數組傳入Set中,創建一個Set實例,這樣就自動完成了過濾,但是這樣還不是數組,我們得再把Set結構轉化為數組,這就用到了es6中的解構賦值,如上。詳情可以看阮一峰老是的es6入門系列。
總結: javascript在不斷的變化,不斷的成熟,將來會有更多的實用函數提供給我們程序員使用~~~
[] + {} 和 {} + []的運算結果(可以先去試一下)
這個問題是在《你不知道的javascript 中卷》中提出的,書中詳細介紹了他們的區別和原因,大家可以去看一下。
1. [] + {}
它返回的是 "[object Object]" ,是這樣一個字符串,很明顯他就是一個原始對象的toString()結果,下面我們來介紹一下原因:
大家都知道在 + 運算符有 數學運算功能也有字符串拼接功能,在這里是字符串拼接,因為 此表達式 [] 和 {} 需要轉化為字符串來進行運算,[]轉化為字符串是空字串,{} 轉化為字符串就是 "[object Object]",所以理所當然的得到了這個結果。
2. {} + []
它返回了數字 0 ,這就奇怪了,按照上面的邏輯,它不應該還是返回 "[object Object]"么?答案是非,這里牽扯到{}的運用。
javascript解析是從左到右, 在[] + {}中,解析成漢語就是 空數字+ 空對象。
但是在 {} + []中,javascript不會這樣解析,因為開始一個{},javascript不認為它是一個空對象,而是把它認為是一個空代碼塊,就像 if 后面跟着的{}一樣,所以javascript解析玩空代碼塊{}后面就是 + [] , 很明顯 ,空數組前面一個+,將數組轉化為數字形式,因此得到的結果就是 0
延伸:代碼塊
在javascript語言{}表示一個代碼塊,類似於c語言,代碼塊中可以放任意行的表達式。如:
// if 代碼塊 if (...) { // .. 代碼塊中放代碼 } // 普通代碼塊,和沒有一樣的結果 { var a = 1; } console.log(a) // 1 // let代碼塊,創建一個塊級作用域 { left a = 1; console.log(a) // 1 } console.log(a) // 報錯
++ [[]][+[]]的運算
這個問題網上也有,在stack overflow 有解答,也有人翻譯成了中文,不過我看到解釋並不好理解。我是在nodejs群看到的,進群問題就是這個,現在每一個進群的人都會問為什么這個結果是 1 ,看到這問題無數遍了~~~。這個運算不難,但是你可能想當然的理解了,並沒有真正的明白,如果你並沒有真正的明白,請看這一節。
首先 [+[]]中 + [] 的結果 是 0 ,沒有疑問吧,
[[]][0] 這不就是取一個數組的第一個元素么? 然后我們運算出來得到[]
現在變成了 ++ [], 糟糕!報錯了,為什么,因為 ++ 不能直接對一個值進行,他需要用變量來做個中轉站。之前基礎篇已經講過++了,這里再次深入一下。
var a = 1; a++ // 1 1++ //報錯,左值無效
以游戲模式通過計算路線圖講解原因:
a++ , a角色的丹田內原先有1點經驗,他又吃了1點經驗的經驗丹, 這樣a角色丹田升級為2點經驗。
1++, 同樣的 某個角色丹田有1點經驗,這個角色背包游1點經驗的經驗丹,但是角色沒有吃,那么它的丹田依舊1點經驗,丹田1 + 背包經驗丹1沒有結果,所以報錯,他們只是兩量存在並沒有權利升級。
結論: ‘值’ 不能作用於 ++, 因為他們無法變化, 1數字存在於一個內存中, 2數字存在於另一個內存, 1++ 不能將自身內存中的1變為2,數組也一樣。
然而變量則是引用了值的量詞,變量 ++ ,是 在變量+ 1 的前提下,使變量升級引用比原先值大1的值。
好,++ [] 在這里 也是 []只是個值,並不是個變量,想給自己升級一下,是不可能的,引擎會報錯。這個運算流程終結。看正確的流程。
++ [[]][0] 這個體系,是一體的,看下面的問答:
問: [[]][0] 是不是等於 [] 這個值?
答: 是
問: 上面示例a++ 中 a 是不是等於 1 這個值?
答: 是
問: 那么,將a這個變量變成他引用的值也就是1, ++1成立么?
答: 不成立,報錯
問: 既然這樣,為啥 ++ [[]][0] 你不能當成 一體計算,非要先算 [[]][0]呢, a引用了1, 你算 a++, [[]][0] 只是換了個方式引用[],為啥你非要算++ [],而不是++ [[]][0]
答: 哦,我明白了,這個 [[]][0] 和 a是一樣的,都是引用了一個值,然后自增1.所以他的結果就是空數組自增1,空數組轉化為Number是0,自增1就等於1了。
問答完了,你應該也懂了吧,廢話很多,是因為更詳細的介紹出來。
到這里,善於嘗試的同學可能會有了一個問題:我在谷歌控制台執行 1 = 1+ 1報錯, 但是 [] = [] + 1為啥沒報錯返回了'1'
那我只能說你很好學,很喜歡動手。
這個問題是為什么呢?那是因為es6的緣故,es6的解構賦值語法,使得[] 在左側成為了一個合法的值。不信你在其他瀏覽器試試。
解構賦值也很簡單:
[a, b] = [1,2] a //1 b // 2 //也可以用字符串 [c,d] = '12' c //1 d// 2
明白這個問題后,再看 [] = [] + 1, 右邊 [] 會變成字符串'', 理所當然得到了 '1', 左邊是空的數組,僅僅是沒有賦值變量而已,整個表達式返回的就是右邊的值‘1’。
結語
語言組織能力差,可能有些地方的語句很糟糕,諒解。。。