【问题标题】:Why doesn't function.apply() work across document boundaries in IE?为什么 function.apply() 在 IE 中不能跨文档边界工作?
【发布时间】:2009-07-09 14:33:13
【问题描述】:

我在 IE 中看到一些奇怪的行为,试图通过 function.apply() 调用另一个页面中的函数。

这是一个简单的测试用例:

test1.html:

<HTML>
<HEAD>
<script language="javascript" type="text/javascript">
  var opened = null;

  function applyNone() {
    opened.testFunc.apply(opened);
  }

  function applyArgs() {
    opened.testFunc.apply(opened, ["applied array"]);
  }

  function call() {
    opened.testFunc("called directly");
  }

  function remoteApply() {
    opened.testApply(["used remote apply"]);
  }

  function remoteApplyCopy() {
    opened.testApplyCopy(["used remote apply copy"]);
  }

  function openPopup() {
    opened = window.open("test2.html", "_blank");
  }
</script>
</HEAD>
<BODY>
  <a href="#" onclick="openPopup()">OPEN</a>
  <hr>
  <a href="#" onclick="applyNone()">applyNone</a>
  <a href="#" onclick="applyArgs()">applyArgs</a>
  <a href="#" onclick="call()">call</a>
  <a href="#" onclick="remoteApply()">remoteApply</a>
  <a href="#" onclick="remoteApplyCopy()">remoteApplyCopy</a>
</BODY>
</HTML>

test2.html:

<HTML>
<HEAD>
<script language="javascript" type="text/javascript">
  function testApply(args) {
    testFunc.apply(this, args);
  }

  function testApplyCopy(args) {
    var a = [];
    for(var i = 0; i < args.length; i++) {
      a.push(args[i]);
    }
    testFunc.apply(this, a);
  }

  function testFunc() {
    var s = "Got: ";
    for(var i = 0; i < arguments.length; i++) {
      s += arguments[i] + " ";
    }
    document.getElementById("output").innerHTML += s + "<BR>";
  }
</script>
</HEAD>
<BODY>
  Hi there
  <div id="output"/>
</BODY>
</HTML>

在 Firefox 和 chrome 中,所有方法都可以正常工作。

在 IE(在 6、7 和 8 中测试)中,除了 applyArgs() 和 remoteApply() 方法之外的所有方法都按预期工作。

applyArgs() 在尝试调用 apply 时给出“JScript object expected”错误(test1.html 第 11 行)。

remoteApply() 在尝试调用 apply 时会给出相同的“JScript object expected”错误(test2.html 第 5 行)。

问题是,我需要能够使用 apply()。我可以通过执行 remoteApplyCopy() 机制之类的方法来解决这个问题,但我试图避免这种情况。为什么 apply() 不起作用?

【问题讨论】:

  • 如果你最终将参数复制到一个数组中,这里有一个更短的方法:var a = Array.prototype.slice.call(arguments, 0);
  • 我尝试通过执行 args.slice() 来使用 slice,但得到了同样的错误。虽然没有尝试过 Array.prototype。
  • 另外,我想我必须在 test2.html 中这样做。我试图避免在目标页面中有任何额外的代码(只是被调用的实际函数)。

标签: javascript internet-explorer apply


【解决方案1】:

您需要在另一个窗口中创建数组,因为每个窗口都有自己的 Array 构造函数。我认为这会奏效。

将此函数添加到test2.html:

function getEmptyArray() {
    return new Array();
}

还有这个函数到test1.html:

Array.prototype.cloneToRemote = function (win) {
    var newArray = win.getEmptyArray();
    for (var i = 0; i < this.length; i++)
    {
        newArray.push(this[i]);
    }
    return newArray;
}

然后这样做:

function applyArgs() {
    opened.testFunc.apply(opened, ["applied array"].cloneToRemote(opened));
}

注意,看来你应该可以做到

var newArray = new win.Array();

在 test1.html cloneToRemote() 函数中,但我无法让它工作。如果你能做到这一点,你就可以摆脱 test2.html 中的新 getEmptyArray() 函数。

【讨论】:

  • 有趣。我没有考虑到每个窗口都会有自己的 Array 构造函数。仔细考虑之后是有道理的,它确实解释了一些行为(尽管我认为它应该仍然可以正常工作,就像在 Firefox 和 Chrome 中一样)。现在没有时间测试这个,但我会记住的。
【解决方案2】:

我不知道为什么会这样,但我在玩弄你的代码并偶然发现了一个解决方案......将 test2 的函数放在 test1 中,它就可以工作了:

<HTML>
<HEAD>
<script language="javascript" type="text/javascript">
  var opened = null;

  function applyArgs() {
    testFunc.apply(opened, ["applied array"]);
  }

  function openPopup() {
    opened = window.open("test2.html", "_blank");
  }

  function testFunc() {
    var s = "Got: ";
    for(var i = 0; i < arguments.length; i++) {
      s += arguments[i] + " ";
    }
    this.document.getElementById("output").innerHTML += s + "<BR>";
  }
</script>
</HEAD>
<BODY>
  <a href="#" onclick="openPopup()">OPEN</a>
  <hr>
  <a href="#" onclick="applyArgs()">applyArgs</a>
</BODY>
</HTML>

如果我还能弄清楚,我会告诉你的(IE 就是那样奇怪)。就像我说的,我只是在玩弄代码。

【讨论】:

  • 是的,看起来问题是数组在页面之间传递时会以某种方式丢失其数组类型信息(因此 remoteApply 失败但 remoteApplyCopy 有效)。
  • 嗯,函数中的“arguments”变量实际上并不是一个数组;它是一个具有长度属性的类数组对象。我也不认为这是核心问题。 IE 错误来自 test1.html,当它首先尝试使用数组调用该函数时。
  • 失败的两个测试是跨页面边界传递一个数组并使用它来调用apply。由于其他一切都有效,因此这似乎是核心问题。就像 IE 试图验证 apply() 的第二个参数并且看到了它认为不是有效对象的东西。
  • 好吧,如果数组真的是问题所在,你能用 call() 代替 apply() 吗?我意识到 apply() 可能更有用,但 call() 对我有用,因为它摆脱了数组。你也可以 call() 一个带参数的对象,而不是传递一个数组参数。
  • 问题是我需要申请。在我使用的实际代码中,参数来自其他地方作为数组,我需要能够将该数组应用于函数。
【解决方案3】:

如果你改变 test2.html testApply() 函数如下:

function testApply() {
    testFunc.apply(this, arguments);
}

remoteApply() 有效。但是,applyArgs() 仍然失败。

【讨论】:

  • 这会改变行为。您的更改将导致 testFunc 获取一个数组作为其参数,而不是数组中的字符串。
【解决方案4】:

"... applyArgs() 在尝试调用 apply 时给出“JScript object expected”错误(test1.html 第 11 行)。 remoteApply() 在尝试调用 apply 时会给出相同的“JScript object expected”错误(test2.html 第 5 行)。 ……”

哪个对象不是“预期”的“JScript 对象”?

(提示:使用调试器)

--DBJ

【讨论】:

    猜你喜欢
    • 2016-11-15
    • 1970-01-01
    • 2018-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-02
    • 2016-05-12
    • 2013-07-09
    相关资源
    最近更新 更多