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); }