views:

202

answers:

6

Let's say we have this code (forget about prototypes for a moment):

function A(){
  var foo = 1;
  this.method = function(){
    return foo;
  }
}
var a = new A();

is the inner function recompiled each time the function A is run? Or is it better (and why) to do it like this:

function method = function(){ return this.foo; }
function A(){
  this.foo = 1;
  this.method = method;
}
var a = new A();

Or are the javascript engines smart enough not to create a new 'method' function every time? Specifically Google's v8 and node.js.

Also, any general recommendations on when to use which technique are welcome. In my specific example, it really suits me to use the first example, but I know thath the outer function will be instantiated many times.

+2  A: 

The method is not recompiled.

The Javascript interpreter will create a new closure object containing inner methods and local variables every time you call the outer methods.

The exact implementation depends on the Javascript engine.

SLaks
Any thoughts on Google v8?
Discodancer
I have no idea.
SLaks
+7  A: 

From what I understand, it is not so much a matter of "compiling" the function as it is having a different "scope" each time it is executed.

The second method you used will always have method from the same scope.

The first method puts method inside the scope of the A() function call. So any information that is inside that scope (var foo, function parameters, etc) are stored in that instance of the functions scope. So, the same function code will be referenced each time, but it will be in a different scope (and therefore a different "object").

gnarf
+1, best and most concise answer on here (and you actually answered the question!).
musicfreak
A: 

I would guess it gets compiled only once... because the "this" keyword refer to the execution context... so the compiler doesn't need to know much about a function to interpret it.

Furthermore, when you declare a variable in the bottom of a function, it is still accessible at the top:

function test()
{
  alert(hello);
  // ...
  var hello = 2;
}

It is the same thing with functions. I would believe with those 2 things in mind, that it doesn't get re-compiled every time a A gets called.

Mike

Mike Gleason jr Couturier
Actually - this should alert `undefined`.The variable declaration is hoisted to the top, but `hello` is not set to `2` until after the alert.
Sean Kinsey
+3  A: 

Yes, you are creating a new Function object at each instantiation of an A object. You can demonstrate this as follows:

function A(){
  var foo = 1;
  this.method = function(){
    return foo;
  }
}
var a = new A();
var b = new A();
alert(a.method == b.method); // Returns false; two different Function objects

If you want to reuse the same Function object, make the method a property of the prototype, not the instances.

function B() {
  this.foo = 1;
}

B.prototype.method = function() {
  return this.foo;
}

var a = new B();
var b = new B();
alert(a.method == b.method); // Returns true; it's the same Function object

Edit: As far as I know, there is no reason to do something like the first version except to create private variables in a JavaScript object. In the original example, foo is private. Nothing can access it directly from outside the object. Unfortunately, when you are instantiating a large number of objects using this technique, it can have an impact on performance and memory footprint.

In my code, I use a naming convention to distinguish between "public" and "private" properties. I name private properties with an underscore as the first character. So if I see something like myObject._someMethod(), I know something is wrong.


Edit2: From http://code.google.com/apis/v8/design.html, I would think that V8 compiles the closure once, when it creates the hidden class which contains the method property.

jhurshman
This is very interesting. Could it be that the equality operator deliberately differentiates the two methods, but they are actually the same one? I know that I can use the prototype, but I want to ignore that for this case.
Discodancer
@Discodancer: The two methods have the same code, but they are different objects, just like if you do something like `a = {}; b = {}; alert(a == b); // false`.
jhurshman
The performance hit for using local ("private") variables is negligible on modern interpreters. I don't know about memory, but I'd bet that it's not large enough to make a huge difference.
musicfreak
In most cases, you're probably correct, musicfreak.At my work, we unfortunately must support some emphatically non-modern interpreters (*cough* IE) which will leak memory at the slightest provocation. Additionally, the web application that I work on is huge and can involve thousands of instances of very complex JS objects.Under those circumstances, any approach that intentionally proliferates closures is likely to be a source of weeks (or months) of memory-leak-hunting fun.But for routine development, the private property via closures approach is fine.
jhurshman
A: 

Imagine a function as just another object, and then when you create a new one, it's just a copy of the old one, with some data variable changed. You don't need to re-parse the object's source for that to happen. A good analogy is functionoids in C++, function objects in Lua, and I don't really know many other languages.

DeadMG
A: 
function A(){
  var foo = 1;
  this.method = function(){
    return foo;
  }
}

can't be compiled into

function method = function(){ return this.foo; }
function A(){
  this.foo = 1;
  this.method = method;
}

because the functionality would change. The first example has a variable 'foo' that is only visible to the constructor 'A' and to the function called 'method' whereas in the second example the accessible to everything that has access to the instance of 'A'.

It is possible to compile it into

function method = function(){ return 1; }
function A(){ 
  this.method = method;
}

But I don't think that any javascript engine out there will go that far, but if you are able to preprocess your files and willing to put in an extra bit of effort, the google closure compiler could go pretty far if it's used in advanced mode.

nxt
My question was whether the first example creates new instances of the function each time I run A(), not whether it can be compiled into the other example.
Discodancer