NaN屬於number,也是一種基本數據類型,只要有一邊是 NaN,那麼結果就是false正則表達式
包裝對象即基本數據類型通過包裝以後獲得的對象,做爲基本類型值的字符串擁有trim等方法,及length屬性,正是因爲JS代碼會對原始值作一次包裝,而後變爲了字符串對象,再執行相關的操做。數組
// "a".trim(); //該過程在JS解析過程當中真實存在 var tmp = new String("a"); tmp.trim();
原始值和包裝對象的區別在於類型不一樣。這是最根本的區別,並且雖然是包裝"對象",但也會有對象的少部分特性,好比:函數
var A = new String("a"); var B = new String("a"); var C = "a"; A == B //false A == C //true
對象能夠分爲三種:純對象(plain object)、實例對象、其餘類型的對象。prototype
純對象指由字面量生成,成員中不含函數和日期、正則表達式等類型的對象。
一元操做符會對對象隱式轉換,對象會先調用valueOf方法,而後是toString方法,直到能轉換爲基本數據類型爲止。code
而判斷兩個對象是否是相等時,由於對象保存在堆內存,只有兩個對象引用同一個地址,纔會相等:對象
{} == {}//false var a = new Object(); var b = a; console.log(a == b)
若是須要比較兩個對象的鍵名鍵值對是否相等,能夠採起JSON.stringify的方法轉換後再比較。接口
經過構造函數生成的對象,這樣的對象和純對象同樣沒法直接進行外部比較是否相等,可使用構造函數(類)提供靜態方法或實例方法來判斷是否相等。內存
指的數組、日期、正則表達式等Object衍生出來的對象,通常須要根據使用場景來構造判斷方法,決定兩個對象是否相等。字符串
例如日期對象要經過Data.prototype.getTime()方法來獲取時間戳判斷是否表示同一個時刻,正則須要toString獲取原始字面量來判斷是不是相同的正則表達式。get
若是判斷元素是否相等的方法中,採用的是==比較運算,兩邊的數據會發生隱式類型轉換,這就形成了影響判斷結果的因素。
在判斷Boolea、Number、String三種類型進行不一樣類型的 == 比較時,規則是將其轉化爲數字以後再比較是否相等。
console.log( "ac" == true )//false console.log(123 == "123");//true
而undefined表示"缺乏值",就是此處應該有一個值,可是尚未定義。它會被轉換成數字,而轉換結果爲NaN,NaN不等於任何值,因此undefined != false;對於null來講,null表示"沒有對象",即該處不該該有值。首先調用Object.valueOf方法返回基本類型值以後在比較,因此null != false
最後一點是undefined == null,這是ECMA-262標準 11.9.3 節的規定。
let arr = [12,12,9,2,0,9,8]; /* 例如:12第一次出如今0,以後再出現時index爲1, 說明第二個是重複值,因此只返回第一個12, 可是對於NaN而言indexOf只會爲-1,因此無論有幾個NaN都會直接跳過 */ function unique(arr){ return arr.filter(function(value,index){ return arr.indexOf(value) === index; }) } //indexOf(NaN)則一直爲-1,數組中會出現一個或多個NaN(若是存在) function unique(arr){ let ret = []; arr.forEach(function(value){ if(ret.indexOf(value) === -1){ ret.push(value); } }) return ret; } console.log(unique(arr));
在規範中,indexOf()使用的是全等比較,只要有NaN都是沒法判斷位置直接跳過的。
全等比較不能處理NaN的相等性判斷,NaN不等於任何值,包括自己。
Array.prototype.includes()是ES6中新增的方法,判斷數組中是否包含某個元素,上一中indexOf方法能夠修改成:
function unique(arr){ let ret = []; arr.forEach(function(value){ if(!ret.includes(value)){ ret.push(value); } }) return ret; }
includes()方法內部的比較方法是:"SameValueZero",詳細規則:
1. If Type(x) is different from Type(y), return false. 2. If Type(x) is Number,then a. If x is NaN and y is NaN, return true. b. If x is +0 and y is -0, return true. c. If x is -0 and y is +0, return true. d. If x is the same Number value as y, return true. e. Return false. 3. Return SameValueNonNumber(x, y).
注意:若是x、y都是NaN,則返回true,因此includes方法能夠判斷是否包含了NaN
var arr = [12,1,"d3",NaN]; console.log(arr.includes(NaN));//true
因而可知indexOf和includes方法對NaN待的行爲不同。
遍歷是最基本也是最容易想到的方案:
function unique(arr){ let isRepeate; let ret = []; for(var i = 0;len = arr.length,i<len;i++){ isRepeate = false; for(var k = i+1;k<len;k++){ if(arr[i] === arr[k]){ isRepeate = true; break; } } if(!isRepeate){ ret.push(arr[i]); } } return ret; }
去重的部分也是全等操做符實現的,因此對於數組中的NaN而言也會都push進入ret以後返回。
Map是一種新的數據類型,就是key的類型沒有限制的對象,它的存取使用單獨的get、set接口。由於使用單獨的接口存取數據,因此不用擔憂key與內置屬性重名,修改上面的方法後獲得:
function unique(arr){ let ret = []; let len = arr.length; let tmp = new Map(); for(let i = 0;i<len;i++){ if( !tmp.get(arr[i]) ){ tmp.set(arr[i],1); ret.push(arr[i]); } } return ret; }
除了Map之外,還有Set這種數據類型,這是一個集合,它不容許重複元素出現。
若是重複添加相同的元素,只會儲存其中的一個,包括NaN在內。若是將這種特性與數組交換,那麼數組就能夠直接去重了。
function unique(arr){ let ret = new Set(arr); return Array.from(set); }