如何判断JavaScript对象创建自当前窗口还是其他窗口?
首先明确:没有通用的标准内置属性,可以让你从任意对象上直接拿到创建它的窗口引用。你遇到的跨窗口instanceof失效问题,以及要找对象所属窗口的需求,可以按下面的方式处理:
优先用跨上下文稳定的类型判断方案,不用找所属窗口
你碰到的xxx instanceof Object返回false,本质是每个浏览器窗口、iframe都是独立的JS执行上下文(Realm),每个上下文都有自己的一套内置构造函数,跨上下文的instanceof本来就会失效,这是JS规范定义的正常行为。
如果只是要正确判断对象类型,完全没必要费力气找对象所属的窗口,用Object.prototype.toString.call就可以跨窗口稳定返回正确的类型标签:
// 不管是当前窗口还是其他窗口创建的对象,类型判断都准确 const getType = (target) => Object.prototype.toString.call(target).slice(8, -1); console.log(getType(ThisObj)); // 输出 History console.log(getType(OtherObj)); // 输出 History console.log(getType({})); // 输出 Object
如果要判断是不是普通对象,在这个基础上补一层原型校验即可,比找窗口再做instanceof判断成本低很多,也更可靠。
同源场景下可通过窗口遍历匹配所属窗口
如果你确实需要拿到对象所属的窗口引用,在两个窗口同源的前提下,可以遍历所有你能拿到句柄的窗口,逐个匹配构造函数定位:
// 注意:你需要自己维护通过window.open打开的窗口句柄集合,浏览器没有提供API枚举所有已打开的窗口 const openedWindows = new Set(); // 每次window.open之后把返回的句柄存到这个集合里 // const newWin = window.open('xxx'); // openedWindows.add(newWin); function getOwnerWindow(target, currentWin = window) { // 先匹配当前窗口 if (target instanceof currentWin.Object) return currentWin; // 遍历当前窗口下的所有iframe for (const frame of currentWin.frames) { try { // 跨源iframe访问会抛错,直接跳过 if (target instanceof frame.Object) return frame; // 递归处理嵌套iframe的场景 const nestedResult = getOwnerWindow(target, frame); if (nestedResult) return nestedResult; } catch (err) { // 跨源错误直接忽略 } } // 遍历所有手动记录的open打开的窗口 for (const win of openedWindows) { try { if (target instanceof win.Object) return win; const nestedResult = getOwnerWindow(target, win); if (nestedResult) return nestedResult; } catch (err) {} } return null; }
这里要注意:浏览器不会暴露所有用户打开的窗口给你,如果你没有提前保存
window.open返回的窗口句柄,是没法遍历到这类窗口的,自然也没法匹配到对应的所属窗口。
DOM关联对象可直接通过DOM链路取窗口
如果你手里的对象是DOM节点,或者是和文档绑定的宿主对象,可以直接通过DOM的内置属性拿到所属窗口:
// DOM节点取所属窗口 const win = domNode.ownerDocument.defaultView;
这个方法只对DOM相关对象有效,history、localStorage这类BOM对象没有统一的公开属性指向所属窗口,兼容性很差,不建议用。
跨源场景没有可行方案
如果两个窗口不同源,上面说的所有方法都没用。同源策略会直接阻止你访问其他源窗口的构造函数、属性,也不可能拿到其他源窗口的合法引用,这是浏览器的强制安全限制,没有绕过的可能。
内容的提问来源于stack exchange,提问作者user3163495

