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

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

structured clone algorithm 是一种序列化算法,用于通过 window.postMessage 在 windows 之间传递数据等。它支持递归对象(与 JSON 不同)但不支持 DOM 节点、函数和错误,以及其他

我想要的是一种检查给定对象是否可以通过结构化克隆算法序列化的简单方法。我可以递归遍历对象并检查每个 属性 是否是 DOM 节点、函数或错误,但这不是一个完整的答案,我想知道是否有更好的方法。

spec开始,我觉得应该是

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 事件侦听器,它将被调用。如果您想避免这种情况,请将值发送给另一个 window。例如,您可以使用 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;
  };
})();