js类型检测方法

js有几种数据类型?

String、Number、Boolean、undefined、Symbol、null、Object(引用类型)

引用类型包括 Array Object Funtion

几种区分类型的方法

  1. typeof: 只能区分前基本类型、symbol和function,不能区分Array、Object、null、Date等等

  2. constructor: 缺点 undefined和null无法检查

    var symbol = Symbol('1')
    var num = 2
    console.log(symbol.constructor === Symbol)
    console.log(num.constructor === Number)
    // 但是undefined 和 null都没有constructor
    
  3. 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);
    
  4. 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()这四个方法

你可能感兴趣的:(js类型检测方法)