12

我需要在另一个 js 文件中使用来自外部 js 文件的 javascript 函数。这基本上是我尝试过的代码:

$.getScript('js/myHelperFile.js');
myHelperFunction();

这不起作用 - 我收到“未定义 myHelperFunction”的错误。

但是,此代码有效:

$.getScript('js/myHelperFile.js', function(){myHelperFunction();});

我特别希望能够以第一种方式做到这一点 - 在我的文件顶部加载文件,然后从那里使用我需要的任何功能。这是可能的,还是我误解了 getScript 的工作原理?

4

4 回答 4

32

“第一种方式”目前*不可能,jQuery.getScript因为它只支持异步操作,因此它在被调用后立即返回。

由于它在脚本尝试调用时在您的脚本下载之前返回myHelperFunction()myHelperFunction因此undefined会导致该错误。

*请参阅此答案底部的更新,了解一种有前途的新方法,该方法最终将允许您编写几乎像您想要的样式一样工作的代码。

您可以使用jQuery.ajax设置asyncfalse的代码来执行此操作,如下所示:

$.ajax({
  url: 'js/myHelperFile.js',
  async: false,
  dataType: "script",
});

myHelperFunction();

正如文档所述:

同步请求可能会暂时锁定浏览器,在请求处于活动状态时禁用任何操作。

这是一件很糟糕的事情。如果提供的服务器myHelperFile.js在任何时候响应缓慢(这在某个时候发生),则页面将变得无响应,直到请求完成或超时。

采用在整个 jQuery 中大量使用的回调样式会是一个更好的主意。

$.getScript('js/myHelperFile.js', function () {
   myHelperFunction();
});

您也不需要使用匿名函数,您可以将代码放在命名函数中:

function doStuffWithHelper() {
   myHelperFunction();
}

$.getScript('js/myHelperFile.js', doStuffWithHelper);

如果您需要在调用之前加载多个依赖项,myHelperFunction则除非您设置某种系统来确定是否在执行代码之前下载了所有依赖项,否则将无法正常工作。然后,您可以在所有调用上设置回调处理程序,.getScript以在运行代码之前检查所有依赖项是否已加载。

var loadedScripts = {
    myHelperFile: false,
    anotherHelperFile: false
};

function doStuffWithHelper() {
    // check to see if all our scripts are loaded
    var prop;
    for (prop in loadedScripts) {
        if (loadedScripts.hasOwnProperty(prop)) {
            if (loadedScripts[prop] === false) {
                return; // not everything is loaded wait more
            }
        }
    }

    myHelperFunction();
}

$.getScript('js/myHelperFile.js', function () {
    loadedScripts.myHelperFile = true;
    doStuffWithHelper();
});
$.getScript('js/anotherHelperFile.js', function () {
    loadedScripts.anotherHelperFile = true;
    doStuffWithHelper();
});

如您所见,这种方法很快就会变得复杂且难以维护。

如果您确实需要加载多个依赖项,您可能最好使用yepnope.js之类的脚本加载器来执行此操作。

yepnope( {
        load : [ 'js/myHelperFile.js', 'js/anotherHelperFile.js' ],
        complete: function () {
            var foo;
            foo = myHelperFunction();
            foo = anotherHelperFunction(foo);
            // do something with foo
        }
} );

Yepnope 使用回调就像.getScript你不能使用你想坚持的顺序风格。这是一个很好的权衡,因为jQuery.ajax连续执行多个同步调用只会使方法问题更加复杂。


2013-12-08 更新

jQuery 1.5 版本提供了另一种纯 jQuery 方式来做这件事。从 1.5 开始,所有 AJAX 请求都返回一个延迟对象,因此您可以这样做:

$.getScript('js/myHelperFile.js').done(function () {
   myHelperFunction();
});

作为异步请求,它当然需要回调。然而,使用 Deferreds 与传统的回调系统相比具有巨大的优势,使用$.when()您可以轻松地将其扩展为加载多个先决条件脚本,而无需您自己的复杂跟踪系统:

$.when($.getScript('js/myHelperFile.js'), $.getScript('js/anotherHelperFile.js')).done(function () {
  var foo;
  foo = myHelperFunction();
  foo = anotherHelperFunction(foo);
  // do something with foo
});

这将同时下载两个脚本,并且仅在它们都下载后才执行回调,就像上面的 Yepnope 示例一样。


2014-03-30 更新

我最近读了一篇文章,让我意识到了一个新的 JavaScript 特性,它可能在未来启用你想要使用的那种程序化但异步的代码!异步函数将使用await关键字暂停async函数的执行,直到异步操作完成。坏消息是它目前计划包含在 ES7 中,两个 ECMAScript 版本相去甚远。

await依赖于您正在等待返回Promise的异步函数。我不确定浏览器实现将如何表现,如果它们需要一个真正的 ES6 Promise 对象,或者其他承诺的实现,比如从 AJAX 调用返回的一个 jQuery 就足够了。如果需要 ES6 Promise,则需要jQuery.getScript使用返回 Promise 的函数进行包装:

"use strict";
var getScript = function (url) {
  return new Promise(function(resolve, reject) {
    jQuery.getScript(url).done(function (script) {
      resolve(script);
    });
  });
};

然后您可以使用它来下载并await在继续之前:

(async function () {
  await getScript('js/myHelperFile.js');
  myHelperFunction();
}());

如果您今天真的决心以这种风格编写,您可以使用asyc然后await使用Traceur将您的代码转换为将在当前浏览器中运行的代码。这样做的额外好处是您还可以使用许多其他有用的 ES6 新特性

于 2011-11-06T11:30:24.363 回答
2

单独使用该getScript()功能,我认为您不能。问题是脚本是使用 AJAX 加载的(实际上,getScript它只是一个特殊$.ajax()函数的简写),当浏览器尝试在下一行执行该函数时,服务器可能还没有返回文件。

如果您不想使用回调函数,则需要使用$.ajax()原始形式的 jQuery 函数(忘记getScript()),并将传输设置为同步。这样,您可以确保在继续执行代码之前加载脚本:

$.ajax({
    url: url,
    dataType: "script",
    async: false
});
于 2011-11-06T09:46:10.037 回答
2

$.getScript,实际上所有与 HTTP OP 相关的 jQuery 函数默认都是非阻塞调用。也就是说,操作完成时代码不会挂起。这意味着您上面的代码肯定会失败,因为 $.getScript 无法在尝试执行该方法的行运行之前加载脚本。这就是为什么回调函数被接受为参数的原因。

但是,您尝试做的事情是可能的。加载脚本后,您应该可以从任何地方调用它。为避免在下载脚本之前调用该方法所需要做的就是设置必要的标志。

var HelperFileLoaded = false; 
$.getScript('js/myHelperFile.js', function() { HelperFileLoaded = true; });

// ...

function btnSaveClick() {
    if(!HelperFileLoaded) return;
    myHelperFunction(); 
}

BenM 的建议是可能的,但除非您只对 myHelperFunction 进行一次调用,否则它不会作为解决方案起作用,因为无法保证在触发任何其他调用 myHelperFunction 的方法之前脚本已经下载。

于 2011-11-06T09:48:25.870 回答
0

如果您使用 JSONP,您的第一个工作正常,我经常这样做。

于 2013-11-22T16:59:14.337 回答