redux中isPlainObject函数的实现

时间:2018-08-07 08:35:50

标签: javascript redux

最近,我正在阅读redux的源代码。但是,我无法在isPlainObject函数中理解此代码:

/**
 * @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
}

我认为它的工作方式类似于下面的代码,您能为我解释一下吗?

return Object.getPrototypeOf(obj) === Object.prototype || Object.getPrototypeOf(obj) === null

2 个答案:

答案 0 :(得分:1)

  

我认为它的工作原理类似于代码return Object.getPrototypeOf(obj) === Object.prototype

是的,这是基本思想。

  

… || Object.getPrototypeOf(obj) === null

不,它不是要检查的内容。

  

你能解释吗?

Object.prototype的相等性比较仅适用于实际上从Object.prototype继承的对象。但是,对于普通对象而言,情况并非总是如此,它可能来自另一个领域(例如iframe)并继承自另一个领域的Object.prototype。为了检测到这些,代码基本上首先在参数的原型链中搜索类似Object.prototype的对象(即从null继承),然后检查参数是否直接从该对象继承。

当然,整个循环过程是不必要的,它们可以(应该)简化为

export default function isPlainObject(obj) {
  if (typeof obj !== 'object' || obj === null) return false

  const proto = Object.getPrototypeOf(obj);
  return proto !== null && Object.getPrototypeOf(proto) === null;
}

答案 1 :(得分:0)

正如函数名称所述,它查找对象是否为普通对象。

javascript 中的普通对象是由 {}、new Object() 或 Object.create(null) 创建的对象。这里是对普通对象 https://www.quora.com/What-is-a-plainObject-in-JavaScript/answer/%E9%A3%9E-%E7%BD%97-1 的更详细解释。

我们要做的就是寻找给定对象的原型是否等于 Object.prototype 或等于 null

代码中

Object.getPrototypeOf(obj) === Object.prototype || Object.getPrototypeOf(obj) === null

如果你想知道为什么我们有空检查,当我们用 Object.create(null) 的原型创建一个对象时 它有一个空值。换句话说:

Object.getPrototypeOf(Object.create(null)) === null => true

现在,如果我们可以像上面的代码片段一样轻松地得到想要的答案,为什么他们要放置一个使代码更难理解的 while 循环?当然,唯一的原因不是让它变得更难:)

当我们尝试跨 iframe 访问变量时,在导入 iframe 的文件和框架文件中创建的普通对象的原型并不相等。这是因为它们在不同的环境中运行。换句话说,对象的原型仍然指向Object构造函数,但Object构造函数并不相等。

为了更好的理解,我们可以演示一下。在同一个文件夹中创建4个文件:index.html frame.html index.js frame.js.

注意:要使下面的示例在没有浏览器阻止跨源框架的情况下工作,您应该将它们放在本地服务器中。或者直接使用 vscode Live Server 扩展

在 index.html 下面将 frame.html 导入为 iframe。它还导入仅包含全局变量 plainObject 的 index.js。另外,frame.html 只导入 frame.js。

在 frame.js 中,我们将 plainObject 变量设为 window.parent.plainObject。这里 plainObject 存在于 index.html 和 index.js 的上下文中,正如我们在第二行 console.log 语句中看到的:window.parent.plainObject 的原型不等于 Object.prototype frame.js 上下文。

这正是 redux 使用 while 循环实现该功能的原因。在 frame.js 中,从第 3 行到第 5 行,我们有与 redux 相同的 while 循环。在该循环之后,我们可以通过查看它和它的 getPrototype 结果之间是否存在任何其他 proto 构造函数来安全地检查 parent 的 plainObject 是否是一个普通对象。第 6 行中的 console.log 语句会检查它。

每个文件的内容:

index.html:

<!DOCTYPE html>
  <body>
    <script src="./index.js"></script>
    <iframe src="frame.html">
  </body>
</html>`

frame.html:

<!DOCTYPE html>
 <body>
   <script src='frame.js'></script>
 </body>
</html>

index.js

var plainObject = {}

frame.js

let parentPlainObject = window.parent.plainObject;

console.log(Object.getPrototypeOf(parentPlainObject) === Object.prototype) // this is equal to false because parentPlainObject's prototype is in another environment

while(Object.getPrototypeOf(parentPlainObject) !== null) {
  parentPlainObject = Object.getPrototypeOf(parentPlainObject)
}

console.log(parentPlainObject === Object.getPrototypeOf(window.parent.plainObject)) // now we can test if parentPlainObject is a plain one or has another proto constructor in prototype chain