数据类型
基本数据类型
Null、 Undefined 、String、 Number、 Boolean、 Symbol、 bigInt
引用数据类型
Object、Array、Function、Data等
存储的区别
基本数据类型直接存储在栈中,按值传递。引用数据类型直接存储在堆中,按引用传递。
null与undefined的区别
null:表示一个值被定义了,定义为空值。
// 典型用法
(1) 作为函数的参数,表示该函数的参数不是对象。
(2) 作为对象原型链的终点。
undefined:表示根本不存在定义。
// 典型用法
(1)变量被声明了,但没有赋值,就等于undefined
(2)调用函数时,应该提供的参数没有提供,该参数等于undefined
(3)对象没有赋值的属性,该属性的值为undefined
(4)函数没有返回值时,默认返回undefined
- null与undefined的检测
null == undefined // true
null === undefined // false
typeof undefined // undefined
typeof null // object
检测数据类型的几种方法
- typeof (
只能判断js的基本数据类型,无法判断数组、null、对象)支持两种语法形式:- 作为运算符:typeof x
- 函数形式:typeof(x)
特殊情况:typeof null ---->object
总结:
- 对于基本类型,除null以外,均可以返回正确的结果(typeof null --->object)
- 对于引用类型,除function以外,一律返回object类型
- 对于null,返回object类型.在js中,如果二进制前三位都为0,就会被`typeof`判断为object,其中null的二进制全是0,故返回值为object。
- 对于function返回function类型
- instanceof (
主要判断一个实例/变量是数组还是对象)
console.log(bool instanceof Boolean);// false
console.log(num instanceof Number);// false
console.log(str instanceof String);// false
console.log(und instanceof Object);// false
console.log(null instanceof Object);// false
console.log(arr instanceof Array);// true
console.log(obj instanceof Object);// true
console.log(fun instanceof Function);// true
var bool2 = new Boolean()
console.log(bool2 instanceof Boolean);// true
var num2 = new Number()
console.log(num2 instanceof Number);// true
var str2 = new String()
console.log(str2 instanceof String);// true
- 判断构造函数(constructor)
构造函数原型上由一个constructor属性指向构造函数自身的。如果在实例上使用constructor时,就会直接使用其构造函数原型的上的该属性,并指向其构造函数。
console.log([].constructor === Array); // true
console.log({}.constructor === Object); // true
- 使用Object.prototype.toString.call()函数 原理:
- “根”原型(Object.prototype)下,有个toString的方法,记录着所有 数据类型(构造函数)
- .call作用是改this指向。让传入的对象,执行 “根”原型的toString方法
首先,取得对象的一个内部属性[[class]],依据这个属性,返回一个类似于'[object Array]'的字符串作为结果。利用这个方法,在配合call,可以取得任何对象的内部属性[[Class]],然后将类型检测转化为字符串比较。
call()方法可以改变this的指向,Object.prototype.toString最终会返回形式如[object,class]的字符串,class指代的时其检测出的数据类型。
console.log(Object.prototype.toString.call(boolean));//[object Boolean]
console.log(Object.prototype.toString.call(number));//[object Number]
console.log(Object.prototype.toString.call(string));//[object String]
console.log(Object.prototype.toString.call(undefined));//[object Undefined]
console.log(Object.prototype.toString.call(null));//[object Null]
console.log(Object.prototype.toString.call(array));//[object Array]
console.log(Object.prototype.toString.call(object));//[object Object]
console.log(Object.prototype.toString.call(Function));//[object Function]
// 去除掉返回值中的object
console.log(Object.prototype.toString.call(Function).slice(8,-1));// Function
slice(startIndex,endIndex),从0开始索引,其中8代表从第8位(包含)开始截取(本例中代表空格后面的位置),-1代表截取到倒数第一位(不含),所以正好截取到[object String]中的String。
- 最好的数据类型检测方法
function type(obj){
return typeof obj !== "object" ? typeof obj :Object.prototype.toString.call(obj).slice(8,-1).toLowerCase();
}
typeof、instanceof的区别
typeof用于基本数据类型的类型判断,无法甄别对象具体类型instanceof用于对象的类型判断,基于原型链上的继承关系
额外补充点(JS中的强制转换规则)
- Number()强制转换
- 布尔值,true转换为1,false转换为0
- 数字返回自身
- null返回0
- undefined返回NaN
- 字符串转换
- 空字符串,转换为0
- 字符串中只包含数字(Ox/0X开头数字可以有正负),将其转换为十进制
- 字符串中包含有效的浮点格式,转换为浮点数
- 以上都不是,返回NaN
- 如果是Symbol,抛出错误
Number(true) // 1
Number(false) // 0
Number('0111') // 111
Number(null) // 0
Number('') // 0
Number('1a') // NaN
Number(-0X11) // -17
Number('0X11') // 17
- Boolean()方法的强制转换规则 undefined,null,false,'',0(+0,-0),NaN转换为false,其他转换为true。
Boolean(0) // false
Boolean(null) // false
Boolean(undefined) // false
Boolean(NaN) // false
Boolean(1) // true
Boolean(12) // true
Boolean('a') // true
-
什么时候自动转换为string类型
- 在没有对象的前提下 字符串的自动转换,主要发生在字符串的加法运算时。当一个值为字符串,另一个值为非字符串,则后者转为字符串
'2' + 1 // '21' '2' + true // '2true' '2' + false // '2false' '2' + null // '2null' '2' + undefined // '2undefined'- 当有对象且与对象+时候
// toString对象 var obj = { toString:function(){ return 'a' } } console.log('2' + obj) // 2a //常规对象 var obj = { a:1, b:2 } console.log('2' + obj) //2[object object] // 几种特殊对象 ‘2’ + {} // '2[object object]' '2' + [] // '2' '2' + function(){} // '2fuction(){}' '2' + ['koala',1] // 2koala,1- string类型转换时,易出错点
var obj = { width:'200' } obj.width + 20 // 20020 -
什么时候自动转换为Number类型
- 有加法运算符,但无String类型时,会优先转换为Number类型
true + 0 // 1 true + true //2 true + false // 1
数组
数组的原生方法有哪些
增加
- Array.prototype.push()在数组的末尾增加一个或多个元素,并返回数组的新长度。
- Array.prototype.unshift()在数组的开头增加一个或多个元素,并返回数组的新长度。
删除
- Array.prototype.pop()删除数组的最后一个元素,并返回这个元素。
- Array.prototype.shift()删除数组的第一个元素,并返回这个元素。
修改
- Array.prototype.splice()在任意的位置给数组添加、替换、删除任意个元素。
查找
- Array.prototype.find()找到第一个满足测试函数的元素并返回那个元素的值,如果找不到,则返回undefined。
- Array.prototype.findIndex()找到第一个满足测试函数的元素并返回那个元素的索引,如果找不到,则返回-1。
- Array.prototype.indexOf()返回数组中第一个与指定值相等的元素的索引,如果找不到这样的元素,则返回-1。
- Array.prototype.lastIndexOf()返回数组中最后一个与指定值相等的元素的索引,如果找不到这样的元素,则返回-1。
排序
- Array.prototype.sort()对数组元素进行排序,并返回当前数组。
如果想按照其他标准进行排序,需要提供比较函数。
- Array.prototype.reverse()颠倒数组中元素的排列顺序。
合并
- Array.prototype.join()连接所有数组元素组成一个字符串
- Array.prototype.concat()返回一个由当前数组和其它若干个数组或者若干个非数组值组合而成的新数组。
遍历
- Array.prototype.forEach()为数组中的每个元素执行一次回调函数。
检测
- Array.prototype.includes()判断当前数组是否包含某制定的值,如果是返回true,否则返回false.
从第6个索引位置查找:
- Array.prototype.every()如果数组中的每个元素都满足测试函数,则返回true,否则返回false。
- Array.prototype.some()如果数组中至少有一个元素满足测试函数,则返回true,否则返回false。
过滤
- Array.prototype.filter()将所有在过滤函数中返回true的数组元素放进一个新数组中并返回。
截取数组
- Array.prototype.slice()抽取当前数组中的一段元素组合成一个新数组。
映射
- Array.prototype.map()返回一个由回调函数的返回值组成的新数组。
数组遍历的方法
for循环
let arr = [1,2,3,4]
for(let j = 0,len=arr.length; j < len; j++) {
console.log(arr[j]); // 1 2 3 4
}
forEach
// 没有返回值
// 参数:value数组中的当前项, index当前项的索引, array原始数组;
// 数组中有几项,那么传递进去的匿名回调函数就需要执行几次;
let arr = [1,2,3,4]
arr.forEach((item,index,array)=>{
console.log(index+':'+arr[index]);
})
// 执行结果
0:1
1:2
2:3
3:4
map
map的回调函数中支持return返回值; 并不影响原来的数组,只是相当于把原数组克隆一份,把克隆的这一份的数组中的对应项改变了。
var ary = [12,23,24,42,1];
var res = ary.map(function (item,index,ary ) {
return item*10;
})
console.log(res);//-->[120,230,240,420,10]; 原数组拷贝了一份,并进行了修改
console.log(ary);//-->[12,23,24,42,1]; 原数组并未发生变化
filter遍历
不会改变原始数组,返回新数组
var arr = [
{ id: 1, value: 2 },
{ id: 2, value: 4 },
{ id: 2, value: 7 },
]
let newArr = arr.filter(item => item.value>2);
console.log(newArr ,arr )
for of 遍历
可以调用break、continue和return语句。
var myArray= [12,23,24,42,1];
for (var value of myArray) {
console.log(value);// 12,23,24,42,1
}
every遍历
every()是对数组中的每一项运行给定函数,如果该函数对每一项返回true,则返回true。如果返回false,就退出遍历.
var arr = [ 1, 2, 3, 4, 5, 6 ];
if(arr.every( function( item, index, array ){
return item > 3;
})){
console.log("满足条件,每一个都大于3" );
}else{
console.log("不满足条件,不是每一个都大于3" );
}
some遍历
some()是对数组中每一项运行指定函数,如果该函数对任一项满足条件,则返回true,就退出遍历;否则返回false。
var arr = [ 1, 2, 3, 4, 5, 6 ];
if(arr.some( function( item, index, array ){
return item > 3;
})){
console.log("");
}else{
console.log("不满足条件,没有大于3的" );
}
reduce
reduce 为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素,接受四个参数:初始值(或者上一次回调函数的返回值),当前元素值,当前索引,调用 reduce 的数组。
var total = [0,1,2,3,4].reduce((a, b)=>a + b); //10
console.log(total)
find
find()方法返回数组中符合测试函数条件的第一个元素。否则返回undefined
let name= [
{
name: '张三',
gender: '男',
age: 20
},
{
name: '王小毛',
gender: '男',
age: 20
},
{
name: '李四',
gender: '男',
age: 20
}
];
function getStu(element){
return element.name == '李四'
}
name.find(getStu)
forEach和map的区别
一、forEach():没有返回值
var array = [1,2,3,4,5,6];
var res = array.forEach(function (item,index,input) {
input[index] = item*10;
})
console.log(res);//--> undefined;
console.log(array);//--> 通过数组索引改变了原数组;[10,20,30,40,50,60]
-
参数:value数组中的当前项,index当前项的索引,array原始数组;
-
数组中有几项,那么传递进去的匿名回调函数就需要执行几次
-
理论上这个方式是没有返回值的,只是遍历数组中的每一项, 不对原来数组进行修改,但是可以自己通过数组的索引来修改原来的数组
二、map():有返回值,可以return出来
var array = [10,34,57,43,76];
var res = array.map(function (item,index,input) {
return item*10;
})
console.log(res); // [100,340,570,430,760]
console.log(array); // 不变 [10,34,57,43,76]
-
参数:value数组中的当前项,index当前项的索引,array原始数组
-
区别:map的回调函数中支持return返回值,return的是啥,相当于把数组中的这一项变为啥(并不影响原来的数组,只是相当于把原数组克隆了一份,把克隆这一份的数组中的对应项改变了 )
for...in,for...of
-
推荐在
循环对象属性的时候,使用for...in,在遍历数组的时候的时候使用for...of。 -
for...in循环出的是key,for...of循环出的是value -
注意,for...of是ES6新引入的特性。修复了ES5引入的for...in的不足
-
for...of不能循环普通的对象,需要通过和Object.keys()搭配使用
for(let index in aArray){
console.log(`${aArray[index]}`);
}
for(var value of aArray){
console.log(value);
}
map
map() 方法创建一个新的数组,其结果是该数组中的每个元素都调用一个提供的函数(callback)后返回的结果。
let newArray = arr.map(function
callback( currentValue[,index[, array]] ){
// return element for newArray
}[, thisArg])
map接受两个参数,一个回掉函数callback,一个回掉函数this的值,其中回掉函数接受三个函数currentValue,index,array;
栗子:
["1","2","3"].map(parseInt)
['1', '2', '3'].map( (item,index) => {
console.log( item,index )
return parseInt( item,index )
})
/*
打印的结果为:
'1' 0
'2' 1
'3' 2
*/
/*
返回值:
parseInt( '1', 0 ) // 1
parseInt( '2', 1 ) // NaN
parseInt( '3', 2 ) // NaN
*/
题目中的map只传入了回掉函数-parseInt,其次,parseInt只接受两个参数string,radix(基数)
let m = parseInt( string[, radix] );
string:要被解析的值。若参数不是字符串,则会被隐式转换成字符串( toString()方法 )。字符串开头的空白符将会被忽略。
radix:一个介于2和36之间的整数(不包含36),表示上述字符串的基数。默认为10。返回值:返回一个整数或者NaN(基数就是类似的进制数)(特别的:当radix为0时会当做默认10进制处理)。
parseInt(100,3) // (1*3^2)+(0*3^1)+(0*3^0) = 9
parseInt(15,3) // 会忽略比3大的数5,直接解析数字1,即(1*3^0) = 1
parseInt(6,3) // 忽略比3大的数字6,解析空字符串,即 NaN
parseInt(250,3) // 忽略比3大的数字5,且数字5后面的也会被忽略,2*3^0 = 2
如何将类数组的变量转化为数组
Array.from()方法
function f(){
return Array.from(arguments);
}
f(1,2,3); // [1,2,3]
数组去重
方法一:ES6 Set去重
let arr = [1,2,3,1,2]
function uniqueArray(arr){
return Array.from(new Set(arr))
}
console.log(uniqueArray(arr)) // [1, 2, 3]
这种方法无法去掉“{}”空对象。
方法二:for循环+splice去重
let arr = [1,2,3,1,2]
function uniqueArray(arr){
for(let i = 0;i<arr.length;i++){
for(let j = i+1;j<arr.length;j++){
if(arr[j] === arr[i]){
arr.splice(j,1);
j--;
}
}
}
return arr
}
console.log(uniqueArray(arr)) // [1, 2, 3]
双层循环,外层循环元素,内层循环时比较值。值相同时,则删去这个值。
方法三:indexOf去重
let arr = [1,2,3,1,2]
function uniqueArray(arr){
if(!Array.isArray(arr)){
return;
}
var array = []
for(let value of arr){
if(array.indexOf(value) === -1){
array.push(value)
}
}
return array;
}
console.log(uniqueArray(arr)) // [1,2,3]
新建一个空的结果数组,for 循环原数组,判断结果数组是否存在当前元素,如果有相同的值则跳过,不相同则push进数组。
方法四:利用sort()函数去重
let arr = [1,2,3,1,2]
function uniqueArray(arr){
if(!Array.isArray(arr)){
return;
}
arr = arr.sort()
var array =[arr[0]]
for(var i = 1;i <arr.length;i++){
if(arr[i] !== arr[i-1]){
array.push(arr[i])
}
}
return array;
}
console.log(uniqueArray(arr))
利用sort()排序方法,然后根据排序后的结果进行遍历及相邻元素比对。
方法五:利用includes,includes检测数组是否有某个值。
let arr = [1,2,3,1,2]
function uniqueArray(arr){
var array = []
for(var i = 0;i<arr.length;i++){
if(!array.includes(arr[i])){
array.push(arr[i])
}
}
return array;
}
console.log(uniqueArray(arr)) // [1,2,3]
方法六:利用filter+hasOwnProperty,hasOwnProperty 判断是否存在对象属性
let arr = [1,2,3,1,2]
function uniqueArray(arr){
var obj = {}
return arr.filter(function(item){
return obj.hasOwnProperty(typeof item + item) ? false :
(obj[typeof item + item] = true)
})
}
console.log(uniqueArray(arr)) // [1,2,3]
方法七:利用filter+ indexOf
let arr = [1,2,3,1,2]
function uniqueArray(arr){
return arr.filter(function(item,index,arr){
// 当前元素,在原始数组中的第一个索引 == 当前索引值,否则返回当前元素
return arr.indexOf(item,0) === index
})
}
console.log(uniqueArray(arr)) // [1,2,3]
方法八:递归法
let arr = [1,2,3,1,2]
function uniqueArray(arr){
let array = arr
let len = array.length;
array.sort()
function loop(index){
if(index >= 1){
if(array[index] === array[index - 1]){
array.splice(index,1)
}
loop(index-1);// 递归loop
}
}
loop(len - 1)
return array;
}
console.log(uniqueArray(arr))
方法九:Map数据结构去重
let arr = [1,2,3,1,2]
function uniqueArray(arr){
let map = new Map()
let array = []; // 数组用于返回结果
for(let i = 0;i < arr.length; i++){
if(map.has(arr[i])){ // 如果有该key值
map.set(arr[i],true)
}else {
map.set(arr[i],false) // 如果没有该key值
array.push(arr[i])
}
}
return array;
}
console.log(uniqueArray(arr)) // [1,2,3]
创建一个空Map数据结构,遍历需要去重的数组,把数组的每一个元素作为key存到Map中。由于Map中不会出现相同的key值,所以最终得到的就是去重后的结果。
方法十:reduce + includes
let arr = [1,2,3,1,2]
function unique(arr){
return arr.reduce((prev,cur) => prev.includes(cur) ? prev : [...prev,cur],[]);
}
console.log(uniqueArray(arr))
多维数组转为一维数组
- 利用Array.prototype.soString()方法
var list = [1,[2,[3]],4,[5]];
console.log(list.toString()); // 1,2,3,4,5
- 利用Array.prototype.join()方法
var list = [1,[2,[3]],4,[5]];
console.log(list.toString()); // 1,2,3,4,5
*** 对上面的返回值加以修改为一个数组***
var list = [1,[2,[3]],4,[5]];
JSON.parse(`[${list.toString()}]`);//[1,2,3,4,5]
JSON.parse(`[${list.join()}]`); // [1,2,3,4,5]
如何克隆一个数组
- 借用concat方法
var arr1 = [1,2,3]
var arr2 = arr1.concat()
- 借用slice方法
var arr1 = [1,2,3]
var arr2 = arr1.slice(0);
找出Array中的最大元素
- 利用Math的max方法
var list = [1,100,22,34]
Math.max.apply(null,list)
- 利用Array的sort方法先排序再取值
var list = [1,22,1122,33]
list.sort((a,b)=>{return a-b;})
如何判断一个变量是否为数组
错误的判断方法
- typeof方法
var list = [1,2,3]
typeof list // object
typeof会直接返回Object,不可以使用typeof检测
- instanceof方法
var list = [1,2,3]
list instanceof Array // true
这种方法并不可靠,原因是Array实质是一个引用,用instanceof方法都是利用和引用地址进行比较的方法来确定的,有局限性。
- constructor方法
var list = [1,2,3]
list.constructor === Array // true
正确的方法
利用Object的toString方法(object.prototype.toString.call()方法) Object的原型对象上有一个toString方法,默认被所有对象继承,返回“【object type】”字符串。但此方法经常被原型链上的同名方法覆盖,需要通过
var list = [1,2,3]
Object.prototype.toString.call(list) // [object Array]
利用ES6的Array.isArray()方法
var list = [1,2,3]
Array.isArray(list) // true