views:

281

answers:

3

I have the following JavaScript function:

function Console() {
    this.Log = function(msg) {
        if (document.getElementById("console")) {
            var console = document.getElementById("console");
            console.innerHTML += msg + "<br/>";
        }
    }
}

Question 1: Why do I need to use new key word?

new Console().Log("hello world");

Why couldn't I just do this?

Console().Log("hello world without using new");

Question 2:

var logger = function() {
    this.log = function(msg) {
        new Console().Log(msg);
        new Console().Log("log initialized");
    }

    this.log2 = function(msg) {
        new Console().Log(msg);
        new Console().Log("log2 initialized");
    }
}(); //notice the brackets

This wouldn't run because of the () at the end of logger.

new logger().log("hello world");

I know with the trailing () it means the function is called immediately but why wouldn’t it work? Is it because function() {} (); can't be assigned to other variables?

+3  A: 
  1. The new keyword creates an instance of your Console object which you can then call the Log method on. If you just call Console() directly, you'll get whatever the return value is for that function. In your case there is none, so undefined. Additionally, if you don't use the new keyword, anything you assign to this within that "class function" will pollute the global scope. So instead of assigning your methods to this, you'd have to use a proxy object which you would return instead.

  2. In your example you're assigning the logger variable to the return value of calling your anonymous function. Again, it doesn't return anything so calling new logger() won't work because you can't instantiate undefined. So, removing the trailing () from the anonymous function will assign the function to logger rather than its return value, which you can then instantiate with new. (You could also use a proxy object again).

In both the above examples, I'd strongly advise using the new keyword rather than creating and returning a proxy object. This takes advantage of Javascript's built in instantiation mechanism and the function prototype chain, and is much faster than object creation.

This blog post by John Resig is worth a read for more information on how "class" instantiation works in Javascript: http://ejohn.org/blog/simple-class-instantiation/

James Wheare
For the first point, the return value won't be 'null', it will be 'undefined'.
SolutionYogi
Yup, fixed already :)
James Wheare
var log = (function() { return proxy object })(); log.log(); Would this still pollute the global scope? This way anonymous function will return an instantiated proxy object without using new in the calling method.
Jeffrey C
No, the scope there would remain local. But you're still using a slower method of instantiation. If you don't want to use the new keyword you can write a wrapper function that does and then returns the instance.
James Wheare
+2  A: 

When you do function(){}() you are defining and calling a function immediately, as you pointed out. Therefore when you have this:

var logger = function(){}();

You are assigning logger to be the return value of that function, not the function itself. In your example, the function returns nothing, therefore logger will be undefined.

Just as a matter of style, when writing anonymous functions which are called immediately, it's pretty standard to wrap the function in parentheses:

var logger = (function() { ... })();

It's just a visual hint to the reader that you are going to be calling that function, and it can save a lot of confusion. Actually, it's rather important to do that in some situations: see the comments below!

nickf
Also, if you leave the parentheses out it causes a syntax error in certain situations.
Breton
It's not always just a visual hint. Putting parenthesis essentially allows overall production to be interpreted as **expression**, not as a statement. For example, if you write `function(){}()` in a context of a statement (such as in function body or in global code) you will get a syntax error. This won't happen with `(function(){})()` of course. See my article on named function expression (http://yura.thinkweb2.com/named-function-expressions/) for more details.
kangax
+2  A: 

Answer 1:

You added the 'Log' function to the 'this'. That's the reason you have to create object from Console function before you can access it.

When you do Console().Log(), you are trying to run Console function and call 'Log' method on the returned object. Because Console function doesn't return anything, it is 'undefined' and hence you can't access the Log method.

Answer 2:

The 'logger' is not a function but result of the anonymous function output.

E.g. var logger = function() { //your code; } ();

Your anonymous function doesn't return anything so logger will be 'undefined'. And for an undefined object, there is no 'log' method.

To solve this problem, do this:

var logger = function() {
    var output = {};
    output.log = function(msg) {
        new Console().Log(msg);
        new Console().Log("log initialized");
    }

    output.log2 = function(msg) {
        new Console().Log(msg);
        new Console().Log("log2 initialized");
    }

    return output;
}();

To use, you can write,

logger.log('hello');

Understanding function and Object creation in JS

The main thing you have to understand is that in JavaScript, a regular function acts as a 'constructor' when called with 'new' keyword.

Consider following function.

function Console()
{
    this['name'] = 'Console'
}

When you call above function like this,

   Console();
   alert(window.name); //alerts 'Console';

'this' will be the 'window' object and it will add 'name' property with the value of 'Console'.

If you call the above function like,

   var output = new Console();
   alert(output.name)  // alerts 'Console'

JavaScript will create a new object which is accessible using 'this' inside 'Console'. Now output will have 'name' property.

Let me know if above doesn't clear up things for you. It's essential that you understand it to be able to take advantage of what JS has to offer.

SolutionYogi
If I use "return this;" would it be any different from return output;?
Jeffrey C
I know this is not very safe and it means different things depending on the context, right? So "return output" seems to be a beter solution?
Jeffrey C
There is no point in doing 'return this' because by using the 'this' you have to use the new FunctionName syntax. Does it make sense?
SolutionYogi
Look at my edit where I explain how a JS function acts as a 'constructor' when called with the 'new' keyword.
SolutionYogi
No, I think it's whether you call "()" immediately or not. The "new" has nothing to do with "return this" or "return output". As I said I think “return output” is safer than “return this”.
Jeffrey C
Read this: http://ejohn.org/blog/simple-class-instantiation/ Or my answer :)
James Wheare
I stand corrected. 'logger' is the output of a function and not a function itself. So the 'new' keyword won't matter here. My bigger point still stands. When you use 'this' inside a function, it will have a different meaning based on whether function was called with the 'new' or it was executed normally.
SolutionYogi