export keyword
Opened this issue · 24 comments
I was wondering if it'd be possible to add an export keyword. The reason is that in some JavaScript environments (hint hint, QtScript) people tend to require certain variable on the global scope. However, with the QtScript transition to V8, adding them to the "this" property will become a strict mode error if you don't use .call on a method, furthermore, many implementations don't posses a window-like object for the global scope.
The solution is to use the bare mode, but this isn't desirable. Instead I would propose the use of a special keyword, export.
a = 1
function randomNumber
...
export function eventAttacked victim, attacker
...
Might compile to:
var eventAttacked;
(function(){
var a;
eventAttacked = function (victim, attacker) { ... }
function randomNumber () { ... }
a = 1;
})();
adding them to the "this" property will become a strict mode error
You mean, the global object is frozen by default in that environment or something? Then you shouldn't be able to add global variables at all.
Running
<script>
"use strict"
var a
Object.freeze(this)
a = 1
</script>
on Firefox 11 gives Error: a is read-only.
"use strict"
function f () {
this.q = 3;
}
f()
console.log(q);
ryan@R26695g /tmp $ node file
node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
TypeError: Cannot set property 'q' of undefined
at f (/tmp/file:4:9)
at Object.<anonymous> (/tmp/file:6:1)
at Module._compile (module.js:432:26)
at Object..js (module.js:450:10)
at Module.load (module.js:351:31)
at Function._load (module.js:310:12)
at Array.0 (module.js:470:10)
at EventEmitter._tickCallback (node.js:192:40)
In strict mode, you can't use this inside a function that's not a property of an object or called with the call method. The compiler wraps all your code in a function. Using the bare option defeats the safeguards.
Edit: Fixed
That's why we use .call(this)
rather than just ()
.
@satyr
Sorry, maybe this(coco example) better shows my point:
"use strict"
this.x = 0
x := 3 # error
x = 4 # wrong
function e
x := 2 # error
this.x = 3 # error
Yes, it can be gotten around, but there are times when your environment does stupid things...
x := 2 # error
How?
this.x = 3 # error
Depends on how e
is called.
Well, you have to remove the line marked #wrong to get the error. It's an "undeclared variable" error.
And I don't want to call e with funny calls every time, or bind every function in my code. I think an export would be a better solution, it's much less coding.
Assuming your intention at this.x = 3
is to change global x
,
global = this
function e
global.x = 3
is what you want.
Isn't that rather verbose?
And the issue is when I want to do that with a function.
do f
export function f
I would have to opt for the much less readable global.f = function ... for every function in my code which needs to be put on the global object.
less readable
global.f = function
On top level that's merely @f = ->
.
I'm not always going to be on the top level, if I want to reference it, I have to make the global object a variable, or call all my functions using .call @
global = this
@a = !(b, c) ->
d
function e
global.a 3, 6
I think export
would be easier, it hurts my eyes to see functions assigned, especially with all that extra stuff...
Edit: Fixed.
@a = (b, c) !->
Try @a = !(b, c) ->
.
The @a
syntax does NOT hoist the functions. I have multiple files, and if I run code in part of it, a function may be in another file. It might end up below code that requires it. A good example is a util file: The functions have to be hoisted, if they aren't, they wont be available to most files. (those that start with anything which comes before u.)
I'm aware you don't like using multiple files and joining them. But we don't all live in a node.js environment where we can require
the other files.
Ah, hoisting. In that case you do need export function
I guess. See #116 and the mockup I wrote there.
Looks good. Though I would make export
work with regular variables as well.
I would personally rather have export
exporting to exports
if it exists (as in the #116 mockup, where __out
is set to exports ? this
).
Currently I'm using a modified version of CoffeeScript where export
sets any expression that can have a name determined for on exports
, which includes variables, classes and assignments. It looks like that:
macro func 'export', (expr) -> assign(
objAccess (value 'exports'),
if expr instanceof Class then expr.determineName()
else if expr instanceof Assign then expr.variable
else if expr instanceof Value then expr
else throw new Error 'Cannot determine export name'
expr
)
$ bin/coffee -bep 'export foo = (a,b) -> c'
var foo;
exports.foo = foo = function(a, b) {
return c;
};
$ bin/coffee -bep 'export bar'
exports.bar = bar;
$ bin/coffee -bep 'export class Baz'
var Baz;
exports.Baz = Baz = (function() {
function Baz() {}
return Baz;
})();
I've been using that for some time now and its been working out quite well for me.
We can further define:
export x, y #=> export x; export y
export {x: y} #=> __out <<< {x: y}
export [x, @y] = z #=> what should this do?
export class Bar
The top level this
equals exports
in Node though, so you'd rather write class @Bar
.
Is it possible to make the whole function return the exported variables? For when some environments use eval on your script...
Like how?
export function f
a()
(function(){
var __returned = false, __result = {}, __out = typeof exports != 'undefined' && exports || this;
function f () {
a();
}
__out.f = f;
if (!__returned ) __result.f = f;
__returned = true;
return __result;
}).call(this);
Faster way might be to make the code return the "this" object, but that might cause problems.
Too bombastic for a relatively narrow use case.
I guess you can just add return this
at the end. Ideally your environment should provide meaningful exports
or this
.
"use return_exports"
?
Then you don't even bother with the other code. (__out)
And for the record, one QtScript environment I script for DOES use eval on your script. It's stupid, I know. They all seem to be stupid somehow, seems to be a thing with programs which use QtScript.
Edit: Just realised this wont work, since there's no way to put the "use return_exports" at the top of the script for sure...
It does sound like hard to make QtScript code reusable in browser/CommonJS.
Maybe you can abuse hoisting:
$ coco -sp
eval Coco.compile """
export a = 1
return exports
function exports then
"""
{ [Function: exports] a: 1 }
export class Bar
The top level
this
equalsexports
in Node though, so you'd rather writeclass @Bar
.
But than the constructor won't be accessible in scopes with different
this
, unless you manually assign it to Bar
too (Bar = class @Bar
)
Hm, this at-name linking is kinda annoying.
the constructor won't be accessible in scopes with different
this
It will:
$ coco -bcs
class @Bar then # same as: @Bar = Bar = class then
var Bar;
this.Bar = Bar = (function(){
Bar.displayName = 'Bar';
var prototype = Bar.prototype, constructor = Bar;
function Bar(){}
return Bar;
}());