如何定义一个 CoffeeScript 类,在第一次实例化时加载 jQuery,然后触发回调?

发布于 2024-12-07 05:50:00 字数 2018 浏览 0 评论 0原文

我对使用 JavaScript 的经典 OOP 还很陌生;这似乎是一个一般的 OOP 问题,但理想情况下我正在寻找使用 CoffeeScript 的实际代码示例来帮助我学习。

我想定义一个可以由任何其他函数/类实例化的类,但仅在第一次实例化时才执行其主要功能。直接的用例是一个可以由其他类调用的 LoadjQuery 类,该类动态处理加载 jQuery,但每个文档只能加载一次。首先实例化此类的代码可以提供选项和设置(jQuery 版本、缩小/开发版本和回调),但如果 jQuery 已加载,则实例化该类的任何其他尝试都将立即触发回调,或者等待它完成加载,然后触发回调。

这是我到目前为止编写的代码(有效但未经测试)。我不确定这是否是最优雅的方式。例如,有没有办法避免使用 @jquery_loading 标志和/或等待 jQuery 完成加载的 setInterval ?前者的存在只是为了防止代码被多次执行,这就是我想找到一种更自然的方法,而不是依赖于标志和条件分支。后者似乎不必要且尴尬,我想到了一种类似于 Google Analytics 的 _gaq 队列的方法(其中构建了待处理操作的队列,一旦 Google Analytics 库加载,它就会通过队列;然后立即处理队列中的后续添加),但我不知道如何实现它,并且可能不需要那么复杂。

# load jQuery dynamically (see: http://code.google.com/apis/libraries/devguide.html#jquery)
class LoadjQuery

  @jquery_loading = false
  @jquery_loaded = false

  constructor: (callback, version = 1, compressed = true) ->
    if @jquery_loading # only load once per document
      if @jquery_loaded
        callback()
      else
        check_jquery = ->
          if @jquery_loaded
            clearInterval check_jquery
            callback()
        setInterval check_jquery, 100
    else
      @jquery_loading = true # set flag
      script = document.createElement 'script'
      script.src = "http://ajax.googleapis.com/ajax/libs/jquery/#{version}/jquery#{if compressed then '.min' else ''}.js"
      script.onload = -> # set up event handler to ensure that jQuery is loaded before the callback is executed
        if not @jquery_loaded
          @jquery_loaded = true # set flag
          callback()
      script.onreadystatechange = -> # alternative event handler needed for Opera and IE
        if not @jquery_loaded and (script.readyState is 'loaded' or script.readyState is 'complete')
          @jquery_loaded = true # set flag
          callback()
      (document.getElementsByTagName('head') or document.getElementsByTagName 'body')[0].appendChild script

我正在寻找的可能是单身人士吗?如果是这样,最佳实践实现是什么(我见过几种不同的方法),您能给出一个代码示例吗?

谢谢!

I'm still pretty new to classical OOP using JavaScript; this seems like a general OOP question, but ideally I'm looking for an actual code example using CoffeeScript to help me learn.

I want to define a class that can be instantiated by any other function/class, but which executes its main functionality only the first time it's instantiated. The immediate use case will be a LoadjQuery class that can be called by an other class, which handles loading jQuery dynamically, but only once per document. The code that instantiates this class first can provide options and settings (jQuery version, minified/dev version, and a callback), but any other attempts to instantiate the class will just fire the callback immediately if jQuery has already been loaded, or wait for it to finish loading, then fire the callback.

Here's the code that I've written so far (valid but untested). I'm not sure if this is the most elegant way. For example, is there a way to avoid having to use the @jquery_loading flag, and/or the setInterval that waits for jQuery to finish loading? The former only really exists to prevent the code from being executed more than once, which is what I'd like to find a more natural approach for rather than relying on a flag and a conditional branch. The latter seems unnecessary and awkward, and I have in mind an approach sort of like Google Analytics's _gaq queue (where a queue of pending operations is built, and once the Google Analytics library has loaded, it works through the queue; subsequent additions to the queue are then processed immediately), but I don't know how to go about implementing that, and it may not need to be that sophisticated.

# load jQuery dynamically (see: http://code.google.com/apis/libraries/devguide.html#jquery)
class LoadjQuery

  @jquery_loading = false
  @jquery_loaded = false

  constructor: (callback, version = 1, compressed = true) ->
    if @jquery_loading # only load once per document
      if @jquery_loaded
        callback()
      else
        check_jquery = ->
          if @jquery_loaded
            clearInterval check_jquery
            callback()
        setInterval check_jquery, 100
    else
      @jquery_loading = true # set flag
      script = document.createElement 'script'
      script.src = "http://ajax.googleapis.com/ajax/libs/jquery/#{version}/jquery#{if compressed then '.min' else ''}.js"
      script.onload = -> # set up event handler to ensure that jQuery is loaded before the callback is executed
        if not @jquery_loaded
          @jquery_loaded = true # set flag
          callback()
      script.onreadystatechange = -> # alternative event handler needed for Opera and IE
        if not @jquery_loaded and (script.readyState is 'loaded' or script.readyState is 'complete')
          @jquery_loaded = true # set flag
          callback()
      (document.getElementsByTagName('head') or document.getElementsByTagName 'body')[0].appendChild script

Is it maybe a singleton that I'm looking for? If so, what's the best-practice implementation (I've seen several different approaches), and can you give a code example to start from?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

风铃鹿 2024-12-14 05:50:00

你把事情搞得太复杂了。

既然您已经在使用他们的 CDN,为什么不使用 Google 的加载程序呢?

<script type="text/javascript" src="https://www.google.com/jsapi?key=INSERT-YOUR-KEY"></script>
<script type="text/coffeescript">
google.load "jquery", "1.6.4"
</script>

这是一个简单的加载器实现:

# load scripts dynamically
class ScriptLoader

    libraries =
        jQuery: "http://ajax.googleapis.com/ajax/libs/jquery/$version/jquery.js"

    constructor: (options..., callback) ->

        [lib, version, compressed] = options
        if @libraries[lib] then lib = @libraries[lib]

        loadCallback = =>
            return if @loaded
            @loaded = true
            callback()

        s = document.createElement 'script'
        s.onload = loadCallback
        s.onreadystatechange = ->
            loadCallback() if /loaded|complete/.test(s.readyState)

        s.src = lib.replace('$version', version)
        if compressed then lib = lib.replace('.js', '.min.js')

        (document.getElementsByTagName('head')?[0] or document.body).appendChild s

您可以使用它,因为

new ScriptLoader 'jQuery', '1.6', -> alert window.jQuery

我遵循您的初始结构,但请记住,您不应该因为对象的副作用而实例化对象;最好有某种在内部使用它的工厂方法:

loadScript 'jQuery', '1.6', -> ...
# or
ScriptLoader.load 'jQuery', -> ...

You're overcomplicating things.

Since you're already using their CDN, why not use Google's loader?

<script type="text/javascript" src="https://www.google.com/jsapi?key=INSERT-YOUR-KEY"></script>
<script type="text/coffeescript">
google.load "jquery", "1.6.4"
</script>

Here's a simple loader implementation:

# load scripts dynamically
class ScriptLoader

    libraries =
        jQuery: "http://ajax.googleapis.com/ajax/libs/jquery/$version/jquery.js"

    constructor: (options..., callback) ->

        [lib, version, compressed] = options
        if @libraries[lib] then lib = @libraries[lib]

        loadCallback = =>
            return if @loaded
            @loaded = true
            callback()

        s = document.createElement 'script'
        s.onload = loadCallback
        s.onreadystatechange = ->
            loadCallback() if /loaded|complete/.test(s.readyState)

        s.src = lib.replace('$version', version)
        if compressed then lib = lib.replace('.js', '.min.js')

        (document.getElementsByTagName('head')?[0] or document.body).appendChild s

You'd use this as

new ScriptLoader 'jQuery', '1.6', -> alert window.jQuery

I'm following your initial structure, but bear in mind you shouldn't instantiate objects for their side effects; best to have some kind of factory method that uses it internally:

loadScript 'jQuery', '1.6', -> ...
# or
ScriptLoader.load 'jQuery', -> ...
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文