On Coffeescript.org:
bawbag = (x, y) ->
z = (x * y)
bawbag(5, 10)
would compile to:
var bawbag;
bawbag = function(x, y) {
var z;
return (z = (x * y));
};
bawbag(5, 10);
compiling via coffee-script under node.js wraps that so:
(function() {
var bawbag;
bawbag = function(x, y) {
var z;
return (z = (x * y));
};
bawbag(5, 10);
}).call(this);
Docs say:
If you'd like to create top-level variables for other scripts to use,开发者_运维百科 attach them as properties on window, or on the exports object in CommonJS. The existential operator (covered below), gives you a reliable way to figure out where to add them, if you're targeting both CommonJS and the browser: root = exports ? this
How do I define Global Variables then in CoffeeScript. What does 'attach them as properties on window' mean?
Since coffee script has no var
statement it automatically inserts it for all variables in the coffee-script, that way it prevents the compiled JavaScript version from leaking everything into the global namespace.
So since there's no way to make something "leak" into the global namespace from the coffee-script side of things on purpose, you need to define your global variables as properties of the global object.
attach them as properties on window
This means you need to do something like window.foo = 'baz';
, which handles the browser case, since there the global object is the window
.
Node.js
In Node.js there's no window
object, instead there's the exports
object that gets passed into the wrapper that wraps the Node.js module (See: https://github.com/ry/node/blob/master/src/node.js#L321 ), so in Node.js what you would need to do is exports.foo = 'baz';
.
Now let us take a look at what it states in your quote from the docs:
...targeting both CommonJS and the browser: root = exports ? this
This is obviously coffee-script, so let's take a look into what this actually compiles to:
var root;
root = (typeof exports !== "undefined" && exports !== null) ? exports : this;
First it will check whether exports
is defined, since trying to reference a non existent variable in JavaScript would otherwise yield an SyntaxError (except when it's used with typeof
)
So if exports
exists, which is the case in Node.js (or in a badly written WebSite...) root will point to exports
, otherwise to this
. So what's this
?
(function() {...}).call(this);
Using .call
on a function will bind the this
inside the function to the first parameter passed, in case of the browser this
would now be the window
object, in case of Node.js it would be the global context which is also available as the global
object.
But since you have the require
function in Node.js, there's no need to assign something to the global
object in Node.js, instead you assign to the exports
object which then gets returned by the require
function.
Coffee-Script
After all that explanation, here's what you need to do:
root = exports ? this
root.foo = -> 'Hello World'
This will declare our function foo
in the global namespace (whatever that happens to be).
That's all :)
To me it seems @atomicules has the simplest answer, but I think it can be simplified a little more. You need to put an @
before anything you want to be global, so that it compiles to this.anything
and this
refers to the global object.
so...
@bawbag = (x, y) ->
z = (x * y)
bawbag(5, 10)
compiles to...
this.bawbag = function(x, y) {
var z;
return z = x * y;
};
bawbag(5, 10);
and works inside and outside of the wrapper given by node.js
(function() {
this.bawbag = function(x, y) {
var z;
return z = x * y;
};
console.log(bawbag(5,13)) // works here
}).call(this);
console.log(bawbag(5,11)) // works here
Ivo nailed it, but I'll mention that there is one dirty trick you can use, though I don't recommend it if you're going for style points: You can embed JavaScript code directly in your CoffeeScript by escaping it with backticks.
However, here's why this is usually a bad idea: The CoffeeScript compiler is unaware of those variables, which means they won't obey normal CoffeeScript scoping rules. So,
`foo = 'bar'`
foo = 'something else'
compiles to
foo = 'bar';
var foo = 'something else';
and now you've got yourself two foo
s in different scopes. There's no way to modify the global foo
from CoffeeScript code without referencing the global object, as Ivy described.
Of course, this is only a problem if you make an assignment to foo
in CoffeeScript—if foo
became read-only after being given its initial value (i.e. it's a global constant), then the embedded JavaScript solution approach might be kinda sorta acceptable (though still not recommended).
You can pass -b option when you compile code via coffee-script under node.js. The compiled code will be the same as on coffeescript.org.
I think what you are trying to achieve can simply be done like this :
While you are compiling the coffeescript, use the "-b" parameter.
-b
/ --bare
Compile the JavaScript without the top-level function safety wrapper.
So something like this : coffee -b --compile somefile.coffee whatever.js
This will output your code just like in the CoffeeScript.org site.
To add to Ivo Wetzel's answer
There seems to be a shorthand syntax for exports ? this
that I can only find documented/mentioned on a Google group posting.
I.e. in a web page to make a function available globally you declare the function again with an @
prefix:
<script type="text/coffeescript">
@aglobalfunction = aglobalfunction = () ->
alert "Hello!"
</script>
<a href="javascript:aglobalfunction()" >Click me!</a>
If you're a bad person (I'm a bad person.), you can get as simple as this: (->@)()
As in,
(->@)().im_a_terrible_programmer = yes
console.log im_a_terrible_programmer
This works, because when invoking a Reference
to a Function
‘bare’ (that is, func()
, instead of new func()
or obj.func()
), something commonly referred to as the ‘function-call invocation pattern’, always binds this
to the global object for that execution context.
The CoffeeScript above simply compiles to (function(){ return this })()
; so we're exercising that behavior to reliably access the global object.
Since coffeescript is rarely used on it's own, you can use global
variable supplied by either node.js or browserify (and any descendants like coffeeify, gulp build scripts, etc).
In node.js global
is global namespace.
In browserify global
is equal to window
.
So, just:
somefunc = ->
global.variable = 123
Personally, I think you're too focused on the fact that in CoffeeScript, you can't say:
let x
in your main code. But there's nothing preventing you from saying:
x = undefined
in your main code, which will create the variable x. I wanted to be sure that when you later refer to (or assign to) x inside a function, that it would be using the very same x, so I wrote this script:
x = undefined
func = () ->
console.log "x = #{x}"
x = 5
console.log "x = #{x}"
func()
console.log "x = #{x}"
which outputs:
x = undefined
x = 5
x = 5
so there is only one x, which you can use inside a function.
精彩评论