Redux源码的src/utils/isPlainObject.js
中有这样一个函数, 它用于判断一个值是否为一个普通的对象(普通对象即直接以字面量形式或调用 new Object()
所创建的对象):
/**
* @param {any} obj The object to inspect.
* @returns {boolean} True if the argument appears to be a plain object.
*/
export default function isPlainObject(obj) {
if (typeof obj !== 'object' || obj === null) return false
let proto = obj
while (Object.getPrototypeOf(proto) !== null) {
proto = Object.getPrototypeOf(proto)
}
return Object.getPrototypeOf(obj) === proto
}
其大致思路为:
- 首先排除掉肯定不是对象的值
- 根据原型链判断其是否是普通对象
具体到代码中, 下面这行代码就是排除掉肯定不是对象的值. 这里有个常见的js知识点需要注意: typeof null
的返回值为 "object"
. 所以只使用 typeof obj !== 'object'
不能将 null
值排除掉. 因此应使用 typeof obj !== 'object' || obj === null
进行判断.
if (typeof obj !== 'object' || obj === null) return false
再往下就是通过原型链判断了. 通过 while
不断地判断 Object.getPrototypeOf(proto) !== null
并执行, 最终 proto
会指向 Object.prototype
. 这时再判断 Object.getPrototypeOf(obj) === proto
, 如果为 true
的话就代表 obj
是通过字面量或调用 new Object()
所创建的对象了.
为了更清晰地展示原型链, 先来看一张图:
首先要明确的是, Object.getPrototypeOf()
方法用于获取一个对象的原型属性指向的是哪个对象. 如图中的原型链, 调用 Object.getPrototypeOf(f)
得到的返回值和访问 f.__proto__
是一样的, 这个值指向 F.prototype
.
假如一个对象是普通对象, 那么这个对象的 __proto__
一定是指向 Object.prototype
的, 而非普通对象, 例如 f
, 其 __proto__
是指向其构造函数的 prototype
属性. 因此比较 Object.getPrototypeOf(obj)
与 proto
相等, 则判定 obj
是普通对象.