【问题标题】:Scope with IIFE in JavascriptJavascript 中 IIFE 的作用域
【发布时间】:2013-01-14 08:55:07
【问题描述】:

我正在使用一个使用 jsonP 来共享跨域信息的脚本。 它运作良好,但我需要将其放入 IIFE。

var domain = "http://example.com/";
var myObj = {
    recupData : function(data){
        if (data.id) {
            console.log(data.id);
        }
    },
    scriptTag : function() {
        var siteOrigin = domain+"check?q=myObj.recupData",
            script = document.createElement('script');
        script.type = 'text/javascript';
        script.async = true;
        script.src = siteOrigin;
        document.getElementsByTagName('HEAD')[0].appendChild(script);
    }
}
myObj.scriptTag();

这行得通! (我只使用了我的全局脚本的一小部分,只是为了向您展示结构,所以如果那里有任何语法错误,那不是重点)。

但是当我把这段代码放在一个 IIFE(一个自调用函数)中时,我遇到了一些麻烦。

(function(){
var domain = "http://example.com/";
var myObj = {
    recupData : function(data){
        if (data.id) {
            console.log(data.id);
        }
    },
    scriptTag : function() {
        var siteOrigin = domain+"check?q=myObj.recupData",
            script = document.createElement('script');
        script.type = 'text/javascript';
        script.async = true;
        script.src = siteOrigin;
        document.getElementsByTagName('HEAD')[0].appendChild(script);
    }
}
myObj.scriptTag();
})();

我收到错误 myObj 未定义,错误来自 scriptTag 方法,我真的不明白为什么在添加 IIFE 之前我无法访问此方法,它不应该改变任何东西,它只是避免污染全局命名空间。 我认为这只是一个上下文问题,但我需要一个解释。

【问题讨论】:

  • @apsillers 是的,这是个好字符串,当你告诉我的回调不使用 myObj 时,你是什么意思?
  • 错误是在脚本附加之后还是之前出现?您附加的脚本代码是否引用了myObj
  • @JAAulde 是的,脚本标签是在我收到错误之前创建的。我只是创建一个 http 请求以从另一个服务器域获取数据。

标签: javascript namespaces scope iife


【解决方案1】:

使用 IIFE 的动机是您的变量名不会“泄漏”到全局范围内。但是,当您使用 JSONP 时,您有义务将至少一个变量公开给全局范围,以便加载的脚本可以调用它(或其方法之一)。这个加载的脚本作为单独的script 标记插入到head 中,处于完全不同的范围内,因此您只能通过全局范围与其共享变量。

例如,假设您对http://mydomain.com/check?q=myObj.recupData 的调用会产生如下响应:

myObj.recupData({"id":123,"more":"stuff"})

这是在一个单独的脚本标签中加载的,就像你写的一样:

<script type="text/javascript">
myObj.recupData({"id":123,"more":"stuff"})
</script>

显然,如果这个调用应该工作,myObj 需要在全局范围内,所以你应该将它的声明移到 IIFE 之外,或者在 window 对象上显式注册它:

// Global declaration
var myObj;
(function(){
    var domain = "http://mydomain.com/";
    myObj = { ... };
    myObj.scriptTag();
})();

// Registering on window
(function(){
    var domain = "http://mydomain.com/";
    // Also get it as a local variable
    // for a minor scope lookup optimization
    var myObj = window.myObj = { ... };
    myObj.scriptTag();
})();

第二个选项可能更有趣,因为无论您将 sn-p 放在哪里,它总是会在全局范围内结束。如果由于某种原因您最终将第一个 sn-p 嵌套在另一个 IIFE 中,您需要记住也要移动 myObj 声明,这可能很麻烦。

【讨论】:

  • 哦,非常感谢,我真的从来没有注意到这个事实。所以你不认为有什么办法可以避免我的对象必须是全局的吗?例如,如果您使用 jQuery jsonP,则可以将其包含在 IIFE 中。
  • @M4nch4k 您的回调不必是“全局”的,因为它可以在另一个对象内命名空间,但它必须可以从全局范围访问。甚至 jQuery 也为全局范围添加了一个回调,命名为 jquery1436746853(我认为它使用时间戳?),但您根本看不到它,因为 jQuery 抽象了您使用它的需要。
  • @M4nch4k jQuery creates a globally accessible function 用于您的 JSONP 请求,它充当委托并将结果存储在变量中。然后它使用data converter 检索这些结果,以便您的回调函数获取正确的数据。由于您的回调是作为函数(而不是名称)传递的,因此它不需要是全局的并且保持其范围。
  • 因此,如果我的整个脚本包含在 IIFE 中,我需要将我的函数 ou 放在我的对象中并像 window.scriptTag = function(data){} 一样调用它,以避免我的 main对象是全局的。
  • @M4nch4k 如果您不想公开myObj,则需要在全局范围内创建一个委托函数,但可以在本地访问myObj,以便您仍然可以使用它.例如:window.myJsonpDelegate = function(data) { myObj.doStuffWith(data); } 如果您需要大量此类委托来处理大量 JSONP 请求,您可以考虑将它们放在单独的命名空间中以减少全局混乱。在任何情况下,您都需要在全局范围内至少放置一些内容,以供加载的脚本访问。
猜你喜欢
  • 2013-05-31
  • 2023-04-04
  • 2014-03-13
  • 2014-07-26
  • 1970-01-01
  • 2021-07-02
  • 2017-01-03
  • 2010-12-01
相关资源
最近更新 更多