2

在我的内容脚本中,我使用Map来跟踪所有打开的弹出窗口。在Map键值对构造如下:ES6地图的关键相等性在Chrome内容脚本中表现异常

问题是,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

您的真实代码是否包含在IIFE中,还是变量实际上是全局范围的?有可能'popup'正在被页面上的东西重新分配? – loganfsmyth

+0

@loganfsmyth我为调试构建了这个简化版本。所以它“是”我的内容脚本中的真实代码。没有IIFE的包装,没有其他的内容脚本,甚至没有背景剧本在肆虐。 – Microloft

+0

虽然这可能是一个浏览器错误,但您可以尝试使用'const'。实际上,如果您不重新分配价值,您应该始终使用它。 – wOxxOm

回答

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

在铬62.0.3175.2(金丝雀)输出是

data 
data 

从bug报告说明

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,它使用不同的机制(一个实例类型检查)上的密钥,以了解如何处理散列码 。此方法为 RemoteContext案例获得正确答案,因此按照预期使用 JSGlobalProxy (source), 上的特殊hash字段。

所有这些都说明了如何解决WeakMap案例。 v8中正在进行的工作最终会让这个消失 (类似于Map如何修正),但是这可能是 周的数量级,直到它被修复为止。如果我们希望尽早修复,我们可以尝试 黑客的事情,使%_ClassOf返回“全球”,但它还不清楚 对我来说有多少工作。

鉴于这已被打破了两个稳定的版本,我 倾向于只是等待它被固定在v8一侧。我已经 暂时降低了优先级,并将其标记为v8错误 上的阻止,但如果看起来有价值,我愿意尽快采取措施。

更新(2017年8月24日)

该错误已标记为固定的。

相关问题