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

Ret*_*sam 7 javascript structured-clone

所述结构化的克隆算法是使用的,除其他事物的序列化算法,通过传递窗口之间的数据window.postMessage。它支持递归对象(与 JSON 不同),但不支持 DOM 节点、函数和错误等

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

Ori*_*iol 5

规范来看,我认为它会是这样的

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

请注意,这有一些限制:

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

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

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

请注意,如果您有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;
  };
})();
Run Code Online (Sandbox Code Playgroud)

  • 如果对象包含循环引用,这个函数不会溢出堆栈吗? (2认同)