问题
Consider this Coffeescript class, in an app where each class lives in its own file.
class Manager extends Person
title: titles["manager"]
If that file is loaded before the "titles" object, an error generated. I'm assuming this is because of Coffeescripts safety wrapper which is performing ".call(this)" when this file is first loaded?
Otherwise, if I were to delay running any code until after the entire page had fully loaded ($(document.ready()), I could be sure that all the javascript files were fully loaded before any code actually ran.
Doesn't this create some annoying load order problems, or am I not doing something correctly?
回答1:
It can't be both an order issue and a wrapper issue. Wrapping something with a function that's run immediately has no effect on order, only on scope.
And if titles
is defined in another file, then the scoping of class Manager
doesn't matter. So, it's an order issue. How is titles
defined?
if I were to delay running any code until after the entire page had fully loaded ($(document.ready()), I could be sure that all the javascript files were fully loaded before any code actually ran.
Not quite. $(document).ready()
(note the parentheses—there is no document.ready
function...) delays a function's execution until all of the page's HTML has been loaded, which doesn't mean all JavaScript has been loaded. Here's the good news: From the standpoint of JavaScript code, it doesn't matter whether other JavaScript files have been loaded, because they're all run in order. (Note: I'm assuming here that you're not doing anything fancy like adding additional <script>
tags from your JavaScript code.) So as long as you have
<script src="titles.js"></script>
<script src="Manager.js"></script>
you can rest assured that Manager.js
will only run after titles.js
has.
Warning! Relying on $(document).ready()
for ordering JS code is a common mistake that can lead to confusion! If your HTML looked like this
<script src="Manager.js"></script>
<script src="titles.js"></script>
where titles.js
creates a global called titles
and Manager.js
looks like this
$(document).ready ->
console.log titles
then the output will sometimes be titles
, and sometimes be undefined
. Why? Because as the docs say,
If
.ready()
is called after the DOM has been initialized, the new handler passed in will be executed immediately.
And the DOM may already have been initialized when the first JS file is run! (In practice, this will tend to happen if the browser has the page's HTML cached.)
So, keep it simple. Just load your scripts in the right order. Remember that, for all practical purposes, your scripts are concatenated together by the browser, in order, into a single JS file.
回答2:
Give a try to CoffeeToaster:
http://github.com/serpentem/coffee-toaster
It'll do exactly what you're looking for by providing import
directive, which is #<<
, i.e.:
#<< foldera/folderb/folderc/myfile
You don't need to inform the .coffee
extension, plus you can turn on the packaging
option to have namespaces facilities, if you're used to something like:
#<< another/package/myclass
class SomeClass extends another.package.MyClass
It has a build system that will output a single javascript file for you, or several (in debug mode) for easing the debugging process.
Take a look on the README
and the usage example at:
https://github.com/serpentem/coffee-toaster/tree/master/usage
回答3:
It is indeed a load order problem, and titles
in Manager
refers to a global in this case, that isn't declared at this point. If you know titles
is declared globally somewhere else, you must manually ensure that script has been loaded first. And yes, the problem may very well be with the safety wrapper. But just as a recommendation: it's there for a good reason. Create yourself a namespace instead that you export to the global object. The global object lives in this
(at definition time) or exports
in Node. I usually put this one-liner on top of scripts that should have easy access to the global object:
root = exports ? window
From there I can put things in the global object, like a namespace:
root.app =
a: 1,
a_fun: -> 1
class root.app.Cls
method: => 1 + 1
root.app.obj = new root.app.Cls
Now, there will be an 'app' object in your global object with a 'class' (so to speak) and and object.
来源:https://stackoverflow.com/questions/7329818/coffeescript-backbone-and-load-order