ES6 Map的关键相等性在Chrome内容脚本中表现得很奇怪

时间:2017-07-30 01:54:51

标签: javascript google-chrome-extension ecmascript-6 content-script

在我的内容脚本中,我使用Map来跟踪所有打开的弹出窗口。 Map中的键值对构造如下:

问题是,Map.prototype.has()Map.prototype.get() 有时会返回意外结果。

// content.js

let map = new Map();
let popup = window.open('https://www.google.com');
let data = {};

map.set(popup, data);

// retrieve data later
window.setTimeout(() => {

  // should return true, but sometimes return false
  console.log(map.has(popup));

  // should return {}, but sometimes return undefined
  console.log(map.get(popup));

}, 3000);

似乎并不总是考虑添加的密钥和参考popup"等于"由于某些原因。而这种模棱两可的情况似乎只存在于内容脚本中。如果上述代码在浏览器的控制台中执行,则map.has()map.get()始终返回正确的值。

所以我的问题是:为什么会这样?它是由一些我不知道的内容脚本的基础机制引起的吗?

1 个答案:

答案 0 :(得分:2)

我可以重现这一点(Chrome 60.0.3112.78,Windows 10,64位)和reported it as a bug。为发现这个做得好!

在Chrome 62.0.3175.2(金丝雀)中无意中修复了该错误,但是我使用WeakMap构建了一个仍然失败的新演示(https://jsfiddle.net/769a0qmu):

var map = new WeakMap();
var popup = window.open("https://google.com"); // requires popups to be enabled
map.set(popup, "data");
window.setTimeout(function() {
  console.log(map.get(popup)); // expect: "data"
  map.set(popup, "modified");
  console.log(map.get(popup)); // expect: "modified"
}, 3000);

在Chrome 62.0.3175.2(金丝雀)中,输出为

data
data

错误报告的说明

  

Comment 10 by adamk

     

V8中的问题似乎是为远程上下文创建的全局变量   返回%_ClassOf()的空字符串。这导致我们走下坡路   查找此类对象的哈希码时的错误路径。

     

Comment 12 by adamk

     

这里是V8内部发生了什么。首先,请注意原始堆栈   溢出报告,这曾经在Map中被破坏,但不再是。   Map和WeakMap过去常常使用相同的逻辑来获取哈希值   对象的代码。该代码看起来像这样(它写的是   内部v8 JS):

function GetExistingHash(key) {
  if (IS_RECEIVER(key) && !IS_PROXY(key) && !IS_GLOBAL(key)) {
    var hash = GET_PRIVATE(key, hashCodeSymbol);
    return hash;
  }
  return %GenericHash(key);
}
     

请注意IS_GLOBAL(key)。这是一个扩展到的宏   %_ClassOf(key) == 'global'。自RemoteContext创建全局   对象返回空字符串,它未通过此检查,因此我们错误   尝试使用普通对象机制来处理哈希码,   而不是全局特定的逻辑(在C ++中处理   运行时函数%GenericHash()

     

在过去的几个月里,v8已经推动了它的实施   从JS中映射方法,这意味着他们不再共享上述内容   WeakMap的逻辑。特别是关于如何找到的决定   哈希码总是直接转到%GenericHash,后者使用a   关键的不同机制(实例类型检查),看看如何   处理哈希码。这种方法得到了正确的答案   RemoteContext的情况,因此使用特殊的hash字段   JSGlobalProxy   (source),   如预期的那样。

     

所有这些都说明了解决WeakMap案例该怎么做的问题。   V8内部正在进行的工作最终将使其消失   (类似于Map如何修复),但这可能是大约的顺序   几个星期,直到它被修复。如果我们想早点修复,我们可以尝试   黑客入侵%_ClassOf返回"全球",但还不清楚   对我来说有多少工作。

     

鉴于两个稳定版本已经被打破了,我还是   倾向于等待它固定在v8侧。我'已经   暂时降低优先级并将其标记为阻止v8错误,   但是,如果看起来值得的话,我愿意尽早做点什么。

更新(2017-08-24)

该错误已被标记为已修复。