isPlainObject 判断一个纯对象

Js Front End

判断是否为对象

其实js中一切都是对象, 但是这里讨论的对象只是一般对象, 非数组/字符串/方法/等等 形如{}, Object.create(), new AnyClass() 等等

typeof

通过typeof arg === 'object'可以得出的是arg字面变量/null/array 因此, 如果要通过typeof判断是否为object, 则要排除nullarray

function isObj(arg) {
  return arg && typeof arg === 'object' && !Array.isArray(val)
}

Object.prototype.toString

使用toString来判断一个对象, 往往能得到更详细的信息

toString.call([]) // [object Array]
toString.call(() => {}) // [object Function]
toString.call(null) // [object Null]
toString.call(undefined) // [object Undefined]
// ...
// 排除了内置的Date, RegExp等等
function isObj(arg) {
  return Object.prototype.toString.call(input) !== '[object Object]'
}

判断纯对象

一般是key/value形式的对象 还是要根据实际情况来确定判断方式 主要是对Object.create以及new AnyClass形式的判断

使用typescript来表示

type PlainObject = {
  [k: string]: any
}

仅包括字面变量

既通过字面变量声明或者new Object方式的对象

function isPlainObj(value) {
  return (
    value && // 排除掉 null
    (typeof value.constructor !== 'function' || // 除了Object外的一些Class
      value.constructor.name === 'Object')
  )
}

isPlainObj({}) // true
isPlainObj(Object.create({})) // false

仅包括字面变量以及Object.create(null)

function isPlainObj(input) {
  // 先判断是否为一般对象
  if (Object.prototype.toString.call(input) !== '[object Object]') {
    return false
  }

  const prototype = Object.getPrototypeOf(input) // 获取原型, 后续判断原型是否为null或者Object
  return prototype === null || prototype === Object.getPrototypeOf({}) // 相当于 prototype === Object.prototype
}

isPlainObj({}) // true
isPlainObj(Object.create({})) // false
isPlainObj(Object.create(null)) // true

包括字面变量以及构造函数生成的对象

// 先判断是否为对象
function isObjectObject(o) {
  return (
    isObject(o) === true &&
    Object.prototype.toString.call(o) === '[object Object]'
  )
}

function isPlainObject(o) {
  var ctor, prot
  // 首先是一个对象, 通过typeof和toString判断
  if (isObjectObject(o) === false) return false

  // If has modified constructor
  // 判断构造函数是否为function
  ctor = o.constructor
  if (typeof ctor !== 'function') return false

  // If has modified prototype
  // 判断原型是否也是一个对象
  prot = ctor.prototype
  if (isObjectObject(prot) === false) return false

  // If constructor does not have an Object-specific method
  if (prot.hasOwnProperty('isPrototypeOf') === false) {
    return false
  }

  // Most likely a plain Object
  return true
}

lodash实现

源码

/**
    isObjectLike就是判断obj !== null && typeof obj === 'object'
    getTag: 就是拿Object.prototype.toString
*/
function isPlainObject(value) {
  if (!isObjectLike(value) || getTag(value) != '[object Object]') {
    return false
  }
  // Object.create(null)
  if (Object.getPrototypeOf(value) === null) {
    return true
  }
  let proto = value
  // 获取最顶级的proto
  while (Object.getPrototypeOf(proto) !== null) {
    proto = Object.getPrototypeOf(proto)
  }
  // 如果最最顶级proto就是value.prototype, 则为true
  // 既Object.prototype
  return Object.getPrototypeOf(value) === proto
}