如何检查一个对象是否可以通过结构化克隆算法克隆

How to check if an object can be cloned by the structured clone algorithm

本文关键字:可以通过 结构化 算法 是否 一个对象 何检查 检查      更新时间:2023-09-26

结构化克隆算法是一种序列化算法,用于通过window.postMessage在窗口之间传递数据。它支持递归对象(与JSON不同),但不支持DOM节点、函数和错误等

我想要的是一个简单的方法来检查一个给定的对象是否可以被结构化克隆算法序列化。我可以递归遍历对象并检查每个属性是否为DOM Node、Function或Error,但这不是一个完整的答案,我想知道是否有更好的方法。

从规范来看,我认为应该是

function canBeCloned(val) {
  if(Object(val) !== val) // Primitive value
    return true;
  switch({}.toString.call(val).slice(8,-1)) { // Class
    case 'Boolean':     case 'Number':      case 'String':      case 'Date':
    case 'RegExp':      case 'Blob':        case 'FileList':
    case 'ImageData':   case 'ImageBitmap': case 'ArrayBuffer':
      return true;
    case 'Array':       case 'Object':
      return Object.keys(val).every(prop => canBeCloned(val[prop]));
    case 'Map':
      return [...val.keys()].every(canBeCloned)
          && [...val.values()].every(canBeCloned);
    case 'Set':
      return [...val.keys()].every(canBeCloned);
    default:
      return false;
  }
}

注意这有一些限制:

  • 我无法检查对象是否有[[DataView]]内部插槽
  • {}.toString不是获得[[Class]]的可靠方法,但却是唯一的方法。
  • 其他规范可以定义如何克隆其他类型的对象

因此,尝试运行算法,看看是否会产生一些错误,可能更可靠:

function canBeCloned(val) {
  try {
    window.postMessage(val,'*');
  } catch(err) {
    return false;
  }
  return true;
}

注意,如果您有一个message事件监听器,它将被调用。如果您想避免这种情况,请将该值发送到另一个窗口。例如,您可以使用iframe创建一个:

var canBeCloned = (function() {
  var iframe = document.createElement('iframe');
  document.body.appendChild(iframe);
  var win = iframe.contentWindow;
  document.body.removeChild(iframe);
  return function(val) {
    try { win.postMessage(val, '*'); }
    catch(err) { return false; }
    return true;
  };
})();