js有几种数据类型?
String、Number、Boolean、undefined、Symbol、null、Object(引用类型)
引用类型包括 Array Object Funtion
几种区分类型的方法
-
typeof: 只能区分前基本类型、symbol和function,不能区分Array、Object、null、Date等等
-
constructor: 缺点 undefined和null无法检查
var symbol = Symbol('1') var num = 2 console.log(symbol.constructor === Symbol) console.log(num.constructor === Number) // 但是undefined 和 null都没有constructor
-
Instanceof: 缺点常用写法的number、string、boolean等不能检测出来,symbol也是无法检查
console.log( 123 instanceof Number, //false 'dsfsf' instanceof String, //false false instanceof Boolean, //false [1,2,3] instanceof Array, //true {a:1,b:2,c:3} instanceof Object, //true function(){console.log('aaa');} instanceof Function, //true undefined instanceof Object, //false null instanceof Object, //false new Date() instanceof Date, //true /^[a-zA-Z]{5,20}$/ instanceof RegExp, //true new Error() instanceof Error //true )
Number,String,Boolean没有检测出他们的类型,但是如果使用下面的写法则可以检测出来:
var num = new Number(123); var str = new String('dsfsf'); var boolean = new Boolean(false);
-
Object.prototype.toString.call(): 是目前最可靠的方法
var toString = Object.prototype.toString; toString.call(123); //"[object Number]" toString.call('abcdef'); //"[object String]" toString.call(true); //"[object Boolean]" toString.call([1, 2, 3, 4]); //"[object Array]" toString.call({name:'wenzi', age:25}); //"[object Object]" toString.call(function(){ console.log('this is function'); }); //"[object Function]" toString.call(undefined); //"[object Undefined]" toString.call(null); //"[object Null]" toString.call(new Date()); //"[object Date]" toString.call(/^[a-zA-Z]{5,20}$/); //"[object RegExp]" toString.call(new Error()); //"[object Error]"
那就用Object.prototype.toString.call写一个检测方法的函数
function isType (type, value) {
return Object.prototype.toString.call(value) === `[object ${type}]`
}
console.log(isType(1, 'Number'))
优化:想让方法更具体,isNumber、isBoolean、isFunction 等
const types = [
'String',
'Number',
'Boolean',
'Function',
'Object',
'Null',
'Array',
'Undefined'
]
function isType (typing) {
return function (value) {
return Object.prototype.toString.call(value) === `[object ${typing}]`
}
}
const utils = {}
types.forEach(type => {
utils[`is${type}`] = isType(type)
})
console.log(utils.isString(123))
console.log(utils.isNumber(123))
优化:柯里化函数,将两个参数分开
const curring = (fn, arr = []) => {
const len = fn.length
return (...args) => {
let newArgs = [...arr, ...args]
if (newArgs.length === len) {
return fn(...newArgs)
} else {
return curring(fn, newArgs)
}
}
}
function isType (type, value) {
console.log(`[object ${type}]`)
return Object.prototype.toString.call(value) === `[object ${type}]`
}
const newIsType = curring(isType)
const isNumber = newIsType('Number')
const isString = newIsType('String')
console.log(isNumber(1)) // true
// 柯里化也解决了经典的面试题 sum(1)(2,3,4)(5) // 15
function sum(a,b,c,d,e) {
return a + b + c + d + e
}
let newSum = curring(sum)
newSum(1)(2,3,4)(5) // 15
深拷贝和浅拷贝
说到类型就不得不说到深拷贝和浅拷贝了,要考虑拷贝不同类型的值,是作为面试中常考的基本知识。
什么是深拷贝和浅拷贝
浅拷贝只是拷贝值,引用类型的数据,则是拷贝其内存地址
深拷贝是拷贝值,对于引用类型的数据,会在内存中开辟新的空间,拷贝到这个空间里。
// ...和Object.assign() 如果是多层的时候就是浅拷贝
let obj = {name:'xx',age:{n:10}} // age实际存的是指针地址0xfff...
let obj1 = {...obj}
obj1.age.n = 200
console.log(obj) // 此时obj.age.n 也变成了200
浅拷贝有几种方法:
-
对象: ..和Object.assign()
-
数据: ...扩张运算符、slice()等
实现深拷贝
有一种深拷贝的方法: JSON.parse(JSON,stringrify(obj))。缺点是: 此方法只拷贝JSON语法的类型,不符合的直接就删掉了,比如undefined。
实现深拷贝注意几点: 类型判断、引用类型拷贝、循环引用
function deepClone (value, map = new WeakMap()) {
if (value == null) return value
if (value instanceof RegExp) return new RegExp(value)
if (value instanceof Date) return new Date(value)
if (typeof value !== 'object') {
return value
}
if (map.has(value)) {
return map.get(value)
}
map.set(value, value)
let obj = new value.constructor()
for (let key in value) {
if (value.hasOwnProperty(key)) {
obj[key] = deepClone(value[key], map)
}
}
return obj
}
var obj = {
a: 'a',
b: 'b',
c: {
num: 0,
name: 'c'
}
}
obj.d = obj
console.log(deepClone(obj))
Map和WeakMap
既然用到了WeakMap,说说他是什么,和Map有什么区别
Map,hash集合,key可以是任意类型,而对象的key只能是字符串
WeakMap只接受对象作为键名(null除外)。
Map的键实际上是和内存地址绑定的,只要内存地址不一样,就是两个键,解决了hash碰撞的问题。
WeakMap的key指向的key不计入垃圾回收机制。意思的不会因为你被引用就不回收你了
它的键名所引用的对象都是弱引用,即垃圾回收机制不将该引用考虑在内。因此,只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存。也就是说,一旦不再需要,WeakMap 里面的键名对象和所对应的键值对会自动消失,不用手动删除引用。另外WeakMap没有遍历操作,只有get()、set()、has()、delete()这四个方法
网友评论