开头一个问题是:什么是 Plain Object?
并没有看到有官方去专门定义它,更可能它只不过是业界的一种通俗叫法,因此也没有严格的定义。但我们在汉语环境里通常叫它“纯对象”。
业界解释:https://www.quora.com/What-is-a-plainObject-in-JavaScript
下面我们来看一下常见 Library 对 isPlainObject 函数的实现。
jQuery jQuery 3.3 版本中的 isPlainObject 定义在这里 。
为便于阅读,核心代码经过整理后如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 function isPlainObject (obj ) { var proto, Ctor if (!obj || {}.toString .call (obj) !== '[object Object]' ) { return false } proto = Object .getPrototypeOf (obj) if (!proto) { return true } Ctor = {}.hasOwnProperty .call (proto, 'constructor' ) && proto.constructor return ( typeof Ctor === 'function' && Function .prototype .toString .call (Ctor ) === Function .prototype .toString .call (Object ) ) }
lodash lodash 4.0.6 版本中的 isPlainObject 定义在这里 。
基本与 jQuery 版本相同,多了一个 Ctor instanceof Ctor 的条件,满足此条件的仅有 Function 和 Object 两个函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function isPlainObject (value ) { if (!value || typeof value !== 'object' || {}.toString .call (value) != '[object Object]' ) { return false } var proto = Object .getPrototypeOf (value) if (proto === null ) { return true } var Ctor = hasOwnProperty.call (proto, 'constructor' ) && proto.constructor return ( typeof Ctor == 'function' && Ctor instanceof Ctor && Function .prototype .toString .call (Ctor ) === Function .prototype .toString .call (Object ) ) }
redux redux 从 4.0.0 开始在测试中使用了 isPlainObject ,代码在这里 。
它的实现比较简单。
1 2 3 4 5 6 7 8 9 10 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 }
我们并没有一个能判断 Plain Object 的清晰逻辑,大概能理出来的思路是:
先判断 obj 本身是否满足我们熟悉的合法对象概念;
判断 obj 的构造函数是不是 Object
至于判断 prototype 是不是 null,无非是一种 shortcut 罢了。