9

结构化克隆算法是一种序列化算法,除其他外,用于通过window.postMessage. 它支持递归对象(与 JSON 不同),但不支持 DOM 节点、函数和错误等

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

4

1 回答 1

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;
  }
}

请注意,这有一些限制:

  • 我无法检查对象是否有 [[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;
  };
})();
于 2015-09-19T22:39:56.747 回答