Defining a variable, difference btw node REPL and .js/require

179 views
Skip to first unread message

Angel Java Lopez

unread,
Jul 7, 2012, 8:11:56 AM7/7/12
to nod...@googlegroups.com
Hi people!

A very simple question. When I write in the Node repl:

this.foo = 'bar';
console.log(foo);

it's ok, foo is defined. But writing that code in file.js and running

node file.js

then foo is not defined.

Or require('file.js'), then foo is not defined.

Any way to define a variable in the "current context"? Apparently, "this" properties and "current context" top variables are the same in REPL, but they are not the same in .js, or inside a required module.

I want to do this in a dynamic way:

name = 'foo';
// ...
this[name] = 'bar';

so

foo = 'bar';

is not a solution in my context.

I could use:

global[name] = 'bar';

but I want to expose the new "current context" top variables only in my "current context" without pollute the global environment.

I encountered this problem when coding a simple HTML DSL:
I want the DSL functions to be accesible as:

require('simpletags').exportsTo(????); // ???? == global? this? Now, I should use global to make it works in any situation

then, use the exported function as functions in current context:

html(body(h1("TheNextBigThing")));

instead

var st = require("simpletags");

st.html(st.body(st.h1("TheNextBigThing")));

Any other way?

TIA

Angel "Java" Lopez
@ajlopez
gh:ajlopez

Mariusz Nowak

unread,
Jul 7, 2012, 8:27:13 AM7/7/12
to nod...@googlegroups.com
@ajlopez get to know how variable scoping in JavaScript works (what is global, how this and how local variable works), and you'll get the answers :)

Angel Java Lopez

unread,
Jul 7, 2012, 9:18:51 AM7/7/12
to nod...@googlegroups.com
Thanks for the suggestion!.... but...  I missing some part of your answer.

I guess I understand the difference btw global, this.property, and var local. And then, I understand why it not works. What I don't understand is how to circumvent/solve the problem. I don't know if your answer is:

a- " you'll get the answers :) " and then, you will know that it's impossible to solve, or too weird
b-  you'll get the answers :) " and it's possible in this (simple) way.....etc...

AFAIK, it's "a". Am I right?

My problem is:

- module1 requires module2
- I want to use the exposed functions of module2 as they were defined in module1, using dynamic names. That is, it's not a solution

var module2 = require('module2');
var Function2 = module2.Function2;

Usually, I did a bit of experiment at REPL. I found that this works:

var name = 'Function2';
this[name] = ...

var obj = new Function2(); // without using this

BUT it's only works at REPL. So, encouraged by this discovery (I expected it will not work), I hoped to make it works in other context.

Now, I understand why it is work in REPL. Notably, in REPL

this == global

so it's possible to emulate

var foo = ...

with something like (pseudocode)

name = 'foo'
var [name] = .....

writing

name = 'foo';
this[name] = ...

and then foo is available "as if it is" a local var.

But inside a running .js, this is not equal to global. I was tricked by REPL ;-)

So, the better solution I found so far is to put something like this in module1:

var fs = require('fs');
var vm = require('vm');

var includeInThisContext = function(path) {
    var code = fs.readFileSync(path);
    vm.runInThisContext(code, path);
}.bind(this);

includeInThisContext(__dirname + '/module2.js');

console.log(foo); // it's defined

where module2  define foo:

foo = {};

BUT IN MY CASE, module2 doesn't define the functions at its own context:
it uses an array to dynamically define and export functions.  I never have

function h1() {
}

function h2() {
}

defined at module2 context. And I don't want this. My DSL defines a function for each HTML tag. 

Now, I want to have these dynamically defined functions from module2 accessible in outer module1, as they were local to it.

I could write inside my module,

var h1 = makeTag('h1');
var h2 = makeTag('h2");
...
....

for dozens of tags, and then use something like includeInThisContext.... but I feel it's too weird.

Apparently, my problem is: I didn't find a way to define a local var with a dynamic name.

The original DSL in Ruby:

use "include dslmodule". And inside the dsl module, it defines dynamically named functions at module top level. So the functions are automatically available to the module that makes the includes. 

That is the "trick" I didn't found how to emulate in Node.js/require/CommonJS world.

Some links in my research:

The better approach I found in these links is to use eval (!!??):
eval("var "+name+ " = ....");

Am I right? the only ways are the above ones? Or is another way?

Any suggestions?

Angel "Java" Lopez


--
Job Board: http://jobs.nodejs.org/
Posting guidelines: https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
You received this message because you are subscribed to the Google
Groups "nodejs" group.
To post to this group, send email to nod...@googlegroups.com
To unsubscribe from this group, send email to
nodejs+un...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/nodejs?hl=en?hl=en

Mariusz Nowak

unread,
Jul 7, 2012, 9:38:21 AM7/7/12
to nod...@googlegroups.com
In modules you can achieve the same by assigning function to global: e.g. `global.foo = function () { .. }` it will work same as `this.foo = function () { ... }` in REPL. but I would strongly discourage this, relying on global scope is bad practice.

I'd say that unless you're using regular require/export logic to share objects between modules, you're doing something wrong. Try to think just with require/exports and then you should quickly find way home. See how module relations in other packages work.

-- 
Mariusz Nowak

Angel Java Lopez

unread,
Jul 7, 2012, 9:55:26 AM7/7/12
to nod...@googlegroups.com
Thanks!

I usually do in your suggested way, and I'm with you. But in this special case, I feel the user of my module could find a bit weird to write

var st = require('simpletags');

st.html(st.head(st.title('...')), st.body(st.h1(....))

instead

html(head(title('....')), body(h1('...'))

in a controlled way (only in its own module). I want to give him/her an option. Ruby has it (as an option, too). It's the first time I found I needed it in Node.js. Usually, I assert "Ruby ceremony > Javascript/Node.js ceremony", but now, I was trapped by this issue.

My best solutions:

a)
var st = require(...); // and use with st.h1, st.h2...

b)
require('simpletags').exportsTo(global); // maybe in browser I could export to windows directly in the module

c)
var simpletags = require('simpletags');
eval(simpletags.exportLocalCode()); // where .exportLocalCode returns a String with "var h1 = simpletags.h1; ... "
c2)
var st = require('simpletags');
eval(st.exportLocalCode('st')); // the local variable name 'st' should be informed to be used in var ...= .... string result

Jorge

unread,
Jul 7, 2012, 9:58:42 AM7/7/12
to nod...@googlegroups.com
On 07/07/2012, at 15:18, Angel Java Lopez wrote:
>
>
> (...) Apparently, my problem is: I didn't find a way to define a local var with a dynamic name. (...)

The only way to declare a local var dinamically is with eval():

var varName= "thisIsATest";
eval("var "+ varName+ "= 27");
console.log(thisIsATest, global.thisIsATest);

-> 27 undefined

But perhaps you should not do that because (I believe that) the mere presence of eval() in a function will turn off most (all?) v8 optimizations... (me thinks).
--
Jorge.

Mariusz Nowak

unread,
Jul 7, 2012, 10:28:51 AM7/7/12
to nod...@googlegroups.com
This is the use case we were missing, you want to do same thing I've once tried to do in domjs https://github.com/medikoo/domjs
Currently I think there's no better solution than polluting global scope.
In domjs I resolved it by introducing dynamic scope ( https://github.com/medikoo/domjs/blob/master/lib/dscope.js ). but it's not great approach as variable assignments live only during function execution, and that's confusing. I think best solution would to be have possibility to run modules in some predefined lexical scope, but it's not that easy to achieve.

Rick Waldron

unread,
Jul 7, 2012, 12:27:03 PM7/7/12
to nod...@googlegroups.com


On Saturday, July 7, 2012 at 10:28 AM, Mariusz Nowak wrote:

This is the use case we were missing, you want to do same thing I've once tried to do in domjs https://github.com/medikoo/domjs
Currently I think there's no better solution than polluting global scope.
In domjs I resolved it by introducing dynamic scope ( https://github.com/medikoo/domjs/blob/master/lib/dscope.js ).

This is a good use case for WeakMaps; they aren't available in v8 stable yet, but would eliminate the leaky references that could be held in the current implementation.

Rick


 

José F. Romaniello

unread,
Jul 7, 2012, 12:46:02 PM7/7/12
to nod...@googlegroups.com
just to add something to the thread, when you are in a file "this" is equals to "exports"

you can try it as:

echo "console.log(this === exports)" | node

2012/7/7 Angel Java Lopez <ajlop...@gmail.com>

Alan Gutierrez

unread,
Jul 7, 2012, 1:29:30 PM7/7/12
to nod...@googlegroups.com
On Sat, Jul 07, 2012 at 10:55:26AM -0300, Angel Java Lopez wrote:

> var st = require('simpletags');
>
> st.html(st.head(st.title('...')), st.body(st.h1(....))
>
> instead
>
> html(head(title('....')), body(h1('...'))
>
> in a controlled way (only in its own module). I want to give him/her an
> option. Ruby has it (as an option, too). It's the first time I found I
> needed it in Node.js. Usually, I assert "Ruby ceremony > Javascript/Node.js
> ceremony", but now, I was trapped by this issue.
>
> My best solutions:
>
> a)
> var st = require(...); // and use with st.h1, st.h2...
>
> b)
> require('simpletags').exportsTo(global); // maybe in browser I could export
> to windows directly in the module
>
> c)
> var simpletags = require('simpletags');
> eval(simpletags.exportLocalCode()); // where .exportLocalCode returns a
> String with "var h1 = simpletags.h1; ... "
> c2)
> var st = require('simpletags');
> eval(st.exportLocalCode('st')); // the local variable name 'st' should be
> informed to be used in var ...= .... string result

You can also do this:

require('simpletags')(function (html, head, title, body, h1) {
html(head(title('....')), body(h1('...'))
});

I'm using function argument names to both request data from the context and
assign it to variables at once in a control flow library I'm building.

https://github.com/bigeasy/cadence

Using the function argument names for key names, I maintain state for a control
flow in a hash table. If one of the functions needs a value, they name it in
their argument declaration list.

Notice on line 12. `stat` is the return value of call to `fs.stat` while `file`,
and `shift` are pulled from the context for use in that particular step of the
control flow.

https://github.com/bigeasy/cadence/blob/master/deltree.js#L1-L18

It's a nice pattern. Works well.

It removes one level of annoyance. You don't need to assign each member of the
`simpletags` hash to a variable, but it isn't the full magic you're looking for,
where all of the variables magically appear.

--
Alan Gutierrez - http://github.com/bigeasy - http://twitter.com/bigeasy

Alan Gutierrez

unread,
Jul 7, 2012, 1:38:42 PM7/7/12
to nod...@googlegroups.com
On Sat, Jul 07, 2012 at 10:55:26AM -0300, Angel Java Lopez wrote:
also:

require('simpletags')(function (tags) {
with (tags) {
html(head(title('....')), body(h1('...'));
}
});

Alan Gutierrez

unread,
Jul 7, 2012, 2:53:05 PM7/7/12
to nod...@googlegroups.com
Or rather:

with (require('simpletags')) {
html(head(title('....')), body(h1('...'));
}

Will not work with `"use strict"`.

https://twitter.com/brendaneich/status/68001466471817216

Not sure how noders feel about that.

Vyacheslav Egorov

unread,
Jul 8, 2012, 6:05:45 AM7/8/12
to nod...@googlegroups.com

WeakMap/Map/Set are available in V8 for quite some time. You just need to run with --harmony.

(Though storing anything in a WeakMap with a string key might lead to a hard to explain memory leaks).

Vyacheslav Egorov

Rick Waldron

unread,
Jul 8, 2012, 8:53:10 AM7/8/12
to nod...@googlegroups.com

On Sunday, July 8, 2012 at 6:05 AM, Vyacheslav Egorov wrote:

WeakMap/Map/Set are available in V8 for quite some time. You just need to run with --harmony.


I'm not sure I understand what the point your trying to make is. I said "stable v8" which is meant to imply "not behind a flag". If there are "hard to explain memory leaks", that's a bug and should be reported.

Why would you use a WeakMap for use case that required strings as keys? This is like using a chainsaw to cut bread.

Rick

Vyacheslav Egorov

unread,
Jul 8, 2012, 9:15:48 AM7/8/12
to nod...@googlegroups.com

Apparently we define 'stable'  differently. It is behind the flag because it is from the next non-finalized JS standard, not because it is crashy or buggy.

As for string keys: I thought you are proposing to use weakmap like that here, sorry I misunderstood.

Implementation has no problem with them (so nothing to report) but primitives can be shared in unexpected ways (e.g. two objects with the property foo hold the same instance of string 'foo'.) so one should always keep that in mind, because value will never be released if key is strongly reachable.

Vyacheslav Egorov

Rick Waldron

unread,
Jul 8, 2012, 10:35:56 AM7/8/12
to nod...@googlegroups.com

On Sunday, July 8, 2012 at 9:15 AM, Vyacheslav Egorov wrote:

Apparently we define 'stable'  differently.

Probably not, but I think you've misunderstood the --harmony flag and while doing so, you've decided to argue with me about it.
 

It is behind the flag because it is from the next non-finalized JS standard,

Yes, I know, I'm _very_ familiar with "the next non-finalized JS standard". 
 

not because it is crashy or buggy.

Actually, this is _exactly_ what it's for; Map and Set are missing their iterator APIs completely, ES module syntax support appears and disappears (when it is available, module identifiers are assigned as undefined), let is only allowed with the "use strict" prologue... There's more and I think we both agree that these issues are "unstable" and "buggy".
 
 

As for string keys: I thought you are proposing to use weakmap like that here, sorry I misunderstood. 

To clarify, I meant that each scope object could be added as a key to a WeakMap with the cache object as it's value instead of the object dance it does now - which would avoid hasOwnProperty checks and object reference leaks. 

Implementation has no problem with them (so nothing to report) but primitives can be shared in unexpected ways (e.g. two objects with the property foo hold the same instance of string 'foo'.)

Strings don't hold any shared references in JavaScript, but I'm  interested in seeing the code you're describing.

so one should always keep that in mind, because value will never be released if key is strongly reachable.

Yes, this is exactly the point of a WeakMap 


Rick

Ben Noordhuis

unread,
Jul 8, 2012, 11:02:11 AM7/8/12
to nod...@googlegroups.com
Being passive aggressive argumentative like that is a great way to
piss off people. Please knock it off, Rick.

Rick Waldron

unread,
Jul 8, 2012, 1:54:51 PM7/8/12
to nod...@googlegroups.com
I'm absolutely not being passive aggressive and I don't appreciate the accusation.  

I responded to every point that I felt needed a response, in some cases agreeing, others disagreeing and offering more explanation where it felt right to do so.

Your accusation here and now is incredibly aggressive and unfair, considering the discussion was purely technical and you made it personal. So, if you have a problem with me, feel free to give me a call on my mobile: 857-540-9264 an we can work it out voice to voice. If I don't answer the first time, leave me a message and I will call you back. 

Rick

Ben Noordhuis

unread,
Jul 8, 2012, 6:12:51 PM7/8/12
to nod...@googlegroups.com
On Sun, Jul 8, 2012 at 7:54 PM, Rick Waldron <waldro...@gmail.com> wrote:
> I'm absolutely not being passive aggressive and I don't appreciate the
> accusation.
>
> I responded to every point that I felt needed a response, in some cases
> agreeing, others disagreeing and offering more explanation where it felt
> right to do so.

Well okay, maybe you are and in that case I apologize. But I suggest
you phrase your emails differently in the future. Telling someone
who's on the V8 team that he misunderstands what the --harmony flag is
about, well...

Brandon Benvie

unread,
Jul 8, 2012, 8:43:26 PM7/8/12
to nod...@googlegroups.com
I wouldn't call the --harmony flag as a whole stable, primarily because the Proxy implementation hasn't be updated to the new spec. I would be hesitant to call Map and Set stable, because the iterator spec is just getting to the point of implementable now and isn't implemented yet in V8. It's not that what functions they have aren't stable, it's just that they lack some key functionality. WeakMap, however, is perfectly stable and feature complete and I would very much love to see that split off and enabled by default.

(also primitives are invalid keys for WeakMaps and throw an error)

Rick Waldron

unread,
Jul 8, 2012, 10:41:11 PM7/8/12
to nod...@googlegroups.com
On Sun, Jul 8, 2012 at 8:43 PM, Brandon Benvie <brandon...@gmail.com> wrote:
I wouldn't call the --harmony flag as a whole stable, primarily because the Proxy implementation hasn't be updated to the new spec. I would be hesitant to call Map and Set stable, because the iterator spec is just getting to the point of implementable now and isn't implemented yet in V8. It's not that what functions they have aren't stable, it's just that they lack some key functionality. WeakMap, however, is perfectly stable and feature complete and I would very much love to see that split off and enabled by default.

(also primitives are invalid keys for WeakMaps and throw an error)


Thanks for the further clarification on this - I'd also like to see WeakMap moved out of the --harmony flag and into stable v8, perhaps there is more then meets the eye.

Vyacheslav Egorov

unread,
Jul 9, 2012, 4:47:02 AM7/9/12
to nod...@googlegroups.com
I still think we have different definitions of stable :-) I only mean
"should not crash and burn, and if crashes and burns --- please
report". You seem to imply "follows latest draft, is not going to
change" as well. Surely by this definition --harmony is not stable, it
might not be up to date and it will change, just like the draft
itself.

It is highly unlikely (though I can't predict the future) that any
part of --harmony will be moved out from under the flag before final
standard is released.

> Map and Set are missing their iterator APIs completely,

There was no iterator api specified at harmony:simple_maps_and_sets
when V8 implemented them. Erik Arvidsson amended the wiki only a month
ago.

> let is only allowed with the "use strict" prologue...

Which AFAIK was perfectly aligned with old opt-in story (aka extended
mode); opt-in however was dropped in favor of 1JS.

> ES module syntax support appears and disappears (when it is available, module identifiers are assigned as undefined),

Fair point. I don't think modules were ever declared working and I am
not sure why --harmony implies --harmony-modules. Need to ask Andreas
about that (he is working on finishing the implementation afaik).

> Strings don't hold any shared references in JavaScript

Yes, they don't. But objects hold strings. obj = {foo:0} holds "foo".
Fortunately spec foresaw these complications and forbade using
primitives as keys as Brandon points out.

--
Vyacheslav Egorov

Rick Waldron

unread,
Jul 9, 2012, 12:25:14 PM7/9/12
to nod...@googlegroups.com
On Mon, Jul 9, 2012 at 4:47 AM, Vyacheslav Egorov <veg...@chromium.org> wrote:
I still think we have different definitions of stable :-) I only mean
"should not crash and burn, and if crashes and burns --- please
report". You seem to imply "follows latest draft, is not going to
change" as well. Surely by this definition --harmony is not stable, it
might not be up to date and it will change, just like the draft
itself.

It is highly unlikely (though I can't predict the future) that any
part of --harmony will be moved out from under the flag before final
standard is released.

Fair enough, I concede that this is a clearer explanation of our different interpretation of the word "stable" and agree to only use the word stable when referring to "should not crash and burn, and if crashes and burns --- please
report"

...And sorry for the confusion.


 

> Map and Set are missing their iterator APIs completely,

There was no iterator api specified at harmony:simple_maps_and_sets
when V8 implemented them. Erik Arvidsson amended the wiki only a month
ago.

Indeed, I'm also on TC39 with Erik, so I watch these updates closely :)

(I'm still the "new guy")
 

> let is only allowed with the "use strict" prologue...

Which AFAIK was perfectly aligned with old opt-in story (aka extended
mode); opt-in however was dropped in favor of 1JS.

> ES module syntax support appears and disappears (when it is available, module identifiers are assigned as undefined),

Fair point. I don't think modules were ever declared working and I am
not sure why --harmony implies --harmony-modules. Need to ask Andreas
about that (he is working on finishing the implementation afaik).

I'm actually not sure that it does -- module syntax appeared briefly in a Canary build, then disappeared, leaving only an "Uncaught SyntaxError: Unexpected identifier" behind, then it appeared again for a bit, but now gone. It will make your head spin :D
 

> Strings don't hold any shared references in JavaScript

Yes, they don't. But objects hold strings. obj = {foo:0} holds "foo".
Fortunately spec foresaw these complications and forbade using
primitives as keys as Brandon points out.

Ah, this is much clearer now - I simply misunderstood your previous statement - apologies for the confusion there.
 

So... *nods in agreement with all of the above listed things* :) 


Rick
Reply all
Reply to author
Forward
0 new messages