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!
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', -> ...
精彩评论