使用 typeof 检查时调用 window.external 上的无参数方法

Posted

技术标签:

【中文标题】使用 typeof 检查时调用 window.external 上的无参数方法【英文标题】:No-argument method on window.external is invoked when checking with typeof 【发布时间】:2011-02-17 09:09:24 【问题描述】:

我正在尝试在 System.Windows.Forms.WebBrowser 控件中显示带有嵌入 javascript 代码的 html 页面。 JavaScript 代码预计将通过window.external 对象与嵌入环境进行交互。在调用window.external 上的方法之前,JavaScript 应该检查该方法的存在。如果它不存在,代码应该调用一个通用的回退方法。

// basic idea
if (typeof(window.external.MyMethod) != 'undefined') 
    window.external.MyMethod(args);
 else 
    window.external.Generic("MyMethod", args);

但是,使用typeof 检查无参数方法似乎已经调用了该方法。也就是说,如果MyMethod 接受任何正数的参数,上面的代码将完美运行;但是,如果MyMethod 是无参数方法,那么表达式typeof(window.external.MyMethod) 将不会检查其类型,但也会调用它。

这种行为有什么变通方法吗?我可以以某种方式转义表达式window.external.MyMethod 以防止发生方法调用吗?

【问题讨论】:

您是否尝试过使用 typeof 作为运算符而不是函数? typeof window.external.MyMethod !== "undefined" 回答此问题的任何人都需要在 IE 上使用 window.external 尝试他们的解决方案——在 Javascript 上测试对象是否存在的常规方法不适用于 window.external 【参考方案1】:

我没有调试你的确切情况,但我相信我的精神力量可以解决这里发生的事情。

JScript 语言对函数的使用 和仅仅提及 函数进行了区分。当你说

x = f;

表示“将对由 f 标识的函数的引用分配给变量 x”。它提到 f.相比之下,

x = f();

使用 f.意思是“调用f标识的函数,并将返回的值赋给x。”

简而言之,JScript 中的函数本质上就是我们认为的 C# 中委托类型的属性

有些语言没有这种区别。在 VBScript 中,如果你说x = f 并且 f 是一个函数,那就意味着调用该函数,与x = f() 相同。 VBScript 并没有在语法上对函数的使用和提及进行严格区分。

这一切的实现方式是我们使用 COM;具体来说,我们使用 OLE 自动化。在调度对象的字段以获取其值时,JScript 引擎会传递表示“属性获取”或“方法调用”的标志,具体取决于它是否被使用或提及。

但是假设您正在调度的对象是在期望从 VB 调用它的情况下编写的。也许它是 用 VB 编写的。 VB 对象说“哦,我明白你在问我这个方法的价值是完全合理和合法的。因为我不明白提到一个方法和使用它之间的区别,我只是调用无论您通过哪个标志”。

我不知道是否有解决方法,但我愿意赌上一美元,即正在发生的事情是被调用的对象假设调用者需要 VB 语义。

【讨论】:

嗨,埃里克,感谢您的出色回复。自从我昨天阅读了您 2004 年的博客评论 (discuss.techinterview.org/default.asp?joel.3.18644.7) 以来,我预计会在幕后发生这样的事情。我想,COM 的东西是在 WebBrowser 控件的某个地方实现的,不是吗?我将无法修补另一个查找处理?或者我可以将一个方法显式标记为“COM 作为方法而不是属性可见”? 如果是这种情况,您可以尝试在您的 C# 类中使用 IDispatch 实现。首先,我会尝试 IDispatchImpl 属性 - 尝试将其设置为 CompatibleImplInternalImpl 并查看任何一种方式是否有效。或者,您可以提供自己的IDispatch 实现——标记您的对象[ClassInterface(ClassInterfaceType.None)] 并让它显式实现IDispatch。我也怀疑(不太清楚).NET 4.0 的IDynamicObject 可能用于IDispatch 映射。【参考方案2】:

我找到了这个

if ('MyMethod' in window.external)

不调用 MyMethod

【讨论】:

但这并没有多大帮助,如果window.external 不存在,脚本将失败。 @funkybro 在这种情况下,您需要做的就是添加一个额外的检查,例如if (window.external && 'MyMethod' in window.external). 是的,但是如果您在下面看到我的回答,那么在 IE 上执行 window.external 本身就会失败。 虽然 Eric 的回答解释了内部工作原理,但这个回答实际上解决了问题。非常感谢!【参考方案3】:

尝试其中一种

/* Methods for feature testing
 * From http://peter.michaux.ca/articles/feature-detection-state-of-the-art-browser-scripting
 */
function isHostMethod(object, property)
    var t = typeof object[property];
    return t == 'function' ||
    (!!(t == 'object' && object[property])) ||
    t == 'unknown';


function isHostObject(object, property)
    return !!(typeof(object[property]) == 'object' && object[property]);


if (isHostObject(window.external, "MyMethod")) ....

【讨论】:

嗨,肖恩,您的 isHostMethod 也不起作用。 object[property] 行仍然调用该方法。【参考方案4】:

这种效果可以在桌面 Internet Explorer 中看到。一个脚本如:

for (var i in window) 
    console.log('window.' + i + ' = ' + window[i]);

一旦达到external 就会失败。

看来window.external 不是一个常规的Javascript 对象,它是一个VBScript 对象。

因此,window.externalwindow['external'] 等语句因此是 VBScript 语句——正如 Eric L 所说,VBScript 中的 window.external 等同于 window.external()

非常混乱。这似乎也是唯一在 Internet Explorer 上以这种方式运行的对象。

对于使用 IE Mobile 的 window.external.Notify() 与 Windows Phone 8 上的本地通信,但还想在 ios 上设置 document.location 的跨浏览器移动 Web 应用程序,测试 window.external.Notify 的存在将看起来合乎逻辑——但由于这个原因不起作用。

我还没有找到防止这种情况的方法;在script 标签中指定type="text/javascript" 时仍然会发生这种情况。

【讨论】:

【参考方案5】:

所以真正唯一的选择是变通方法: 1)只是不要使用0参数函数。 2)做一个检查函数(带一个参数)来检测是否有外部调用。

if (typeof(window.external.HasExternal(null)) != 'undefined')

并检查外部函数是否都存在而不是函数。 3) 对于您想要的每个无参数函数,如果外部代码支持的功能有所不同,要么使其使用虚假参数,要么为其添加 1 参数检查函数。

【讨论】:

【参考方案6】:

我实际上不确定它是否会有所帮助,但请尝试window.external["MyMethod"]

如果这没有帮助,请尝试将此值存储在一个变量中,然后再检查该变量的类型。看看有没有帮助。

【讨论】:

不幸的是,这两种变体都不起作用。 window.external["MyMethod"]var f = window.external["MyMethod"] 调用它。

以上是关于使用 typeof 检查时调用 window.external 上的无参数方法的主要内容,如果未能解决你的问题,请参考以下文章

安装时调用 UninstallDelete 的检查函数

用 typeof 真正检查是不是未定义 [重复]

为啥要使用 toString() 对可以使用 typeof 进行检查的参数进行类型检查?

如何在单击网格单元格时调用 js 函数?

JavaScript12_函数1:函数的参数,箭头函数的参数

当页面在 Angular 10 中完全加载时调用组件函数