“Can't execute code from a freed script” 错误的产生原因是什么?

66
我之前认为自己找到了解决方案(请参考我的博客):

如果你曾经遇到过JavaScript(或者说是JScript)错误“无法从已释放的脚本中执行代码”,请尝试将head标签中的任何meta标签移到script标签之前。

但是根据最近的一条博客评论,我建议的修复方法可能并不适用于所有人。我认为这是一个向StackOverflow社区开放的好问题...

是什么导致了错误“无法从已释放的脚本中执行代码”,有哪些解决方案或变通方法?

13个回答

36

当您调用在已不存在的窗口或框架中创建的函数时,会出现此错误。

如果您事先不知道窗口是否仍存在,可以使用try/catch检测它:

try
{
  f();
}
catch(e)
{
  if (e.number == -2146823277)
    // f is no longer available
    ...
}

2
谢谢Sjoerd..但为什么这只会在IE中发生呢? - ManJan
这取决于浏览器的实现。我认为在IE中,每个窗口都有自己的JavaScript引擎。在较新的浏览器中(可能也适用于较新的IE版本),当两个窗口访问彼此的代码时,存在共享引擎,因为一个窗口是由另一个窗口打开的。 - Sjoerd Visscher
2
我在AngularJS中遇到了这个问题,我将一个数组移动到$rootScope中,以便我知道我将可以访问该数组。然而,最初设置我的值的数组我不再能够访问。使用angular.copy为我提供了一个新的数组实例,而不是指向我无法访问的旧变量的指针。 - JabberwockyDecompiler
我通过使用 JSON.parse(JSON.stringify(whatever)) 的副本(而不是 angular.copy)来解决了这个问题。它只影响 ie11。 - Harry Pehkonen
2
我会使用0x800a1393而不是十进制整数-2146823277,因为在Google中搜索十六进制值会返回更多结果。此外,在您给出的示例中,是否有可能检查函数f以查找其声明上下文,以记录日志? - mbomb007
显示剩余2条评论

20

当脚本的“父”窗口被处理(即关闭),但仍在另一个窗口中保持对脚本的引用时调用,则会导致错误。即使该“对象”仍然存在,它想要执行的上下文也不存在。

这有点不好看,但对于我的Windows侧边栏小工具有效:

这是一般想法: “主”窗口设置一个将评估某些代码的函数,是的,它很丑。 然后,“子”可以调用此“构建器函数”(它绑定到主窗口的作用域/)并返回一个也绑定到“主”窗口的函数。显而易见的缺点是,被“重新绑定”的函数不能闭合其表面上定义的作用域...无论如何,够了废话:

这部分是伪代码,但我在Windows侧边栏小工具上使用了它的变体(我一直这么说,因为侧边栏小工具在“非受限区域0”中运行,这可能会——或者不会——大大改变情况。)


// This has to be setup from the main window, not a child/etc!
mainWindow.functionBuilder = function (func, args) {
  // trim the name, if any
  var funcStr = ("" + func).replace(/^function\s+[^\s(]+\s*\(/, "function (")
  try {
    var rebuilt
    eval("rebuilt = (" + funcStr + ")")
    return rebuilt(args)
  } catch (e) {
    alert("oops! " + e.message)
  }
}

// then in the child, as an example
// as stated above, even though function (args) looks like it's 
// a closure in the child scope, IT IS NOT. There you go :)
var x = {blerg: 2}
functionInMainWindowContenxt = mainWindow.functionBuilder(function (args) {
  // in here args is in the bound scope -- have at the child objects! :-/
  function fn (blah) {
    return blah * args.blerg
  }
  return fn
}, x)

x.blerg = 7
functionInMainWindowContext(6) // -> 42 if I did my math right

作为一种变体,主窗口应该能够将 functionBuilder 函数传递到子窗口 - 只要 functionBuilder 函数在主窗口上下文中定义!我感觉自己用了太多的词。你的感受可能会有所不同。

1
我刚刚遇到了错误。我正在做类似于上面代码的事情——iframe调用父窗口作用域内的js。哈哈哈,很有趣 :) - Daniel Brink
1
由于我刚刚花了大约两天的时间来解决这个问题...让我注意到,您可以在IE中使用SignalR的foreverFrame传输机制时遇到此类错误。消息(对象)在iFrame中接收/创建,然后传递给主应用程序。稍后可能会处理创建这些对象的iFrame,然后您将获得随机错误...例如,在先前的SignalR消息中调用obj.hasOwnProperty() - jandersen

8

如果你想访问JS对象,最简单的方法是创建一个副本:

var objectCopy = JSON.parse(JSON.stringify(object));

希望这能有所帮助。


请注意,如果对象包含具有函数值的属性,则此方法不会复制它们。 - Alexandru Severin

8

这里有一个非常具体的情况,我在IE6和IE7中看到了这种行为,且可以重现。

在一个iframe内部:

window.parent.mySpecialHandler = function() { ...work... }

然后,在重新加载包含新内容的iframe后,在包含该iframe的窗口中:
window.mySpecialHandler();

这个调用失败并报错“Can't execute code from a freed script”,因为mySpecialHandler是在一个已经不存在的上下文(iframe原始的DOM)中定义的。(重新加载iframe会摧毁这个上下文。)
但你可以安全地在父窗口中设置“可序列化”的值(基本类型和不直接引用函数的对象图)。如果你确实需要一个单独的窗口(在我的情况下,是一个iframe)来指定一些工作给远程窗口,你可以将工作作为字符串传递并在接收者中“eval”它。但要小心使用,一般情况下这不会产生干净或安全的实现。

7
这个错误可能会在MSIE中发生,当子窗口试图与已关闭的父窗口通信时会出现。
(这并不是世界上最有帮助的错误消息文本。)

2
或者反之(从父到子) - erlando
1
当子窗口调用父窗口的函数并传递参数后关闭自身时,我会遇到这个问题。似乎参数(由子窗口创建的对象)在子窗口关闭后立即被释放,尽管它已经传递给父窗口,但父窗口在子窗口关闭后无法访问它。 - Charlie Martin

5

在IE9中,当调用存储在另一个对象内部数组中的Date对象上的.getTime()方法时,我们开始收到此错误。解决方案是在调用Date方法之前确保它是一个Date对象:

失败:rowTime = wl.rowData[a][12].getTime()

成功:rowTime = new Date(wl.rowData[a][12]).getTime()


4
是的,IE9会篡改类型。有时候这也会发生在数组中。如果需要,您可以使用Array.prototype.slice.call(data)。有时它会抛出错误,有时会抛出E_UKNOWN ... 这通常是由于不当调用“delete”导致的虚假误报,与子/父问题无关;这是浏览器因某些问题而疯狂,并给您一个误导信息。 - kristopolous

2

当我在子框架内向顶层窗口添加引用类型,并在子窗口重新加载后尝试访问它时,我遇到了这个问题。

例如:

// set the value on first load
window.top.timestamp = new Date();

// after frame reloads, try to access the value
if(window.top.timestamp) // <--- Raises exception
...

我能够通过使用原始类型解决问题。
// set the value on first load
window.top.timestamp = Number(new Date());

1

这并不是一个答案,而是一个例子,说明了这种情况的具体发生位置。

我们有框架A和框架B(这不是我的想法,但我必须接受它)。框架A永远不会改变,框架B经常变化。我们无法直接将代码更改应用于框架A,因此(根据供应商的说明)我们只能在框架B中运行JavaScript - 正在不断变化的确切框架。

我们有一段需要每5秒运行一次的JavaScript代码,因此框架B中的JavaScript创建了一个新的脚本标记,并将其插入到框架B的头部部分。setInterval存在于这个新脚本中(被注入的脚本),以及要调用的函数。即使被注入的JavaScript技术上是由框架A加载的(因为它现在包含脚本标记),一旦框架B发生变化,该函数就不再可由setInterval访问。


0

之前的回答中解释得非常相关。我只是想提供我的情况,希望这能帮助其他人。

我们曾经使用:

<script> window.document.writeln(table) </script>

在脚本中使用onchange事件调用其他函数,但是在IE中,使用writeln会完全覆盖HTML,而在Chrome中则有不同的行为。

我们将其更改为:

<script> window.document.body.innerHTML = table;</script> 

因此保留了修复问题的脚本。


0

更新iframe的src时,我得到了这个错误。

通过像这样访问主窗口中元素的事件(例如我的点击事件),直接调用主/最外层窗口,导致了该错误:

top.$("#settings").on("click",function(){
    $("#settings_modal").modal("show");
}); 

我刚刚像这样更改了它,现在它可以正常工作(调用 iframe 窗口的父元素的父元素):

$('#settings', window.parent.parent.document).on("click",function(){                    
   $("#settings_modal").modal("show");      
});

我的包含模态框的 iframe 也在另一个 iframe 中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接