5

I have been doing a lot of research on this lately, but have yet to get a really good solid answer. I read somewhere that a new Function() object is created when the JavaScript engine comes across a function statement, which would lead me to believe it could be a child of an object (thus becoming one). So I emailed Douglas Crockford, and his answer was:

Not exactly, because a function statement does not call the compiler.

But it produces a similar result.

Also, to my knowledge, you can't call members on a function constructor unless it has been instantiated as a new object. So this will not work:

function myFunction(){
    this.myProperty = "Am I an object!";
}
myFunction.myProperty; // myFunction is not a function
myFunction().myProperty; // myFunction has no properties

However, this will work:

function myFunction(){
    this.myProperty = "Am I an object!";
}
var myFunctionVar = new myFunction();
myFunctionVar.myProperty;

Is this just a matter of semantics... in the whole of the programming world, when does an object really become an object, and how does that map to JavaScript?

8 Answers8

13

There is nothing magical about functions and constructors. All objects in JavaScript are … well, objects. But some objects are more special than the others: namely built-in objects. The difference lies mostly in following aspects:

  1. General treatment of objects. Examples:
    • Numbers and Strings are immutable (⇒ constants). No methods are defined to change them internally — new objects are always produced as the result. While they have some innate methods, you cannot change them, or add new methods. Any attempts to do so will be ignored.
    • null and undefined are special objects. Any attempt to use a method on these objects or define new methods causes an exception.
  2. Applicable operators. JavaScript doesn't allow to (re)define operators, so we stuck with what's available.
    • Numbers have a special way with arithmetic operators: +, -, *, /.
    • Strings have a special way to handle the concatenation operator: +.
    • Functions have a special way to handle the "call" operator: (), and the new operator. The latter has the innate knowledge on how to use the prototype property of the constructor, construct an object with proper internal links to the prototype, and call the constructor function on it setting up this correctly.

If you look into the ECMAScript standard (PDF) you will see that all these "extra" functionality is defined as methods and properties, but many of them are not available to programmers directly. Some of them will be exposed in the new revision of the standard ES3.1 (draft as of 15 Dec 2008: PDF). One property (__proto__) is already exposed in Firefox.

Now we can answer your question directly. Yes, a function object has properties, and we can add/remove them at will:

var fun = function(){/* ... */};
fun.foo = 2;
console.log(fun.foo);  // 2
fun.bar = "Ha!";
console.log(fun.bar);  // Ha!

It really doesn't matter what the function actually does — it never comes to play because we don't call it! Now let's define it:

fun = function(){ this.life = 42; };

By itself it is not a constructor, it is a function that operates on its context. And we can easily provide it:

var context = {ford: "perfect"};

// now let's call our function on our context
fun.call(context);

// it didn't create new object, it modified the context:
console.log(context.ford);           // perfect
console.log(context.life);           // 42
console.log(context instanceof fun); // false

As you can see it added one more property to the already existing object.

In order to use our function as a constructor we have to use the new operator:

var baz = new fun();

// new empty object was created, and fun() was executed on it:
console.log(baz.life);           // 42
console.log(baz instanceof fun); // true

As you can see new made our function a constructor. Following actions were done by new:

  1. New empty object ({}) was created.
  2. Its internal prototype property was set to fun.prototype. In our case it will be an empty object ({}) because we didn't modify it in any way.
  3. fun() was called with this new object as a context.

It is up to our function to modify the new object. Commonly it sets up properties of the object, but it can do whatever it likes.

Fun trivia:

  • Because the constructor is just an object we can calculate it:

    var A = function(val){ this.a = val; };
    var B = function(val){ this.b = val; };
    var C = function(flag){ return flag ? A : B; };
    
    // now let's create an object:
    var x = new (C(true))(42);
    
    // what kind of object is that?
    console.log(x instanceof C); // false
    console.log(x instanceof B); // false
    console.log(x instanceof A); // true
    // it is of A
    
    // let's inspect it
    console.log(x.a); // 42
    console.log(x.b); // undefined
    
    // now let's create another object:
    var y = new (C(false))(33);
    
    // what kind of object is that?
    console.log(y instanceof C); // false
    console.log(y instanceof B); // true
    console.log(y instanceof A); // false
    // it is of B
    
    // let's inspect it
    console.log(y.a); // undefined
    console.log(y.b); // 33
    
    // cool, heh?
    
  • Constructor can return a value overriding the newly created object:

    var A = function(flag){
      if(flag){
        // let's return something completely different
        return {ford: "perfect"};
      }
      // let's modify the object
      this.life = 42;
    };
    
    // now let's create two objects:
    var x = new A(false);
    var y = new A(true);
    
    // let's inspect x
    console.log(x instanceof A); // true
    console.log(x.ford);         // undefined
    console.log(x.life);         // 42
    
    // let's inspect y
    console.log(y instanceof A); // false
    console.log(y.ford);         // perfect
    console.log(y.life);         // undefined
    

    As you can see x is of A with the prototype and all, while y is our "naked" object we returned from the constructor.

Eugene Lazutkin
  • 43,776
  • 8
  • 49
  • 56
12

Your understanding is wrong:

myFunction().myProperty; // myFunction has no properties

The reason it does not work is because ".myProperty" is applied to the returned value of "myFunction()", not to the object "myFunction". To wit:

$ js
js> function a() { this.b=1;return {b: 2};}
js> a().b
2
js> 

Remember, "()" is an operator. "myFunction" is not the same as "myFunction()". You don't need a "return" when instanciang with new:

js> function a() { this.b=1;}
js> d = new a();
[object Object]
js> d.b;
1
niXar
  • 670
  • 5
  • 13
  • hey is that a command-line javascript interpreter? where can i get one? seems a lot easier than using firebug console to test something – matt b Dec 16 '08 at 19:02
  • 3
    Just did "yum install js"; it's actually spidermonkey, Firefox js engine compiled as a command line executable. If you use a lesser operating system, you can always use rhino, the Java version. – niXar Dec 16 '08 at 22:05
5

To answer your specific question, technically functions are always objects.

For instance, you can always do this:

function foo(){
  return 0;
}
foo.bar = 1;
alert(foo.bar); // shows "1"

Javascript functions behave somewhat like classes in other OOP languages when they make use of the this pointer. They can be instantiated as objects with the new keyword:

function Foo(){
  this.bar = 1;
}
var foo = new Foo();
alert(foo.bar); // shows "1"

Now this mapping from other OOP languages to Javascript will fail quickly. For instance, there is actually no such thing as classes in Javascript - objects use a prototype chain for inheritance instead.

if you're going to do any sort of significant programming in Javascript, I highly recommend Javascript: The Good Parts by Crockford, that guy you emailed.

Kenan Banks
  • 207,056
  • 34
  • 155
  • 173
4

The "global" scope of Javascript (at least in a browser) is the window object.

This means that when you do this.myProperty = "foo" and call the function as plain myFunction() you're actually setting window.myProperty = "foo"

The second point with myFunction().myProperty is that here you're looking at the return value of myFunction(), so naturally that won't have any properties as it returns null.

What you're thinking of is this:

function myFunction()
{
    myFunction.myProperty = "foo";
}

myFunction();
alert(myFunction.myProperty); // Alerts foo as expected

This is (almost) the same as

var myFunction = new Function('myFunction.myProperty = "foo";');
myFunction();

When you use it in the new context, then the "return value" is your new object and the "this" pointer changes to be your new object, so this works as you expect.

Greg
  • 316,276
  • 54
  • 369
  • 333
  • 1
    This is probably the best description of that concept I have ever read. Thanks for posting! –  Dec 17 '08 at 13:50
4

Indeed, Functions are 'first class citizens': they are an Object.

Every object has a Prototype, but only a function's prototype can be referenced directly. When new is called with a function object as argument, a new object is constructed using the function object's prototype as prototype, and this is set to the new object before the function is entered.

So you could call every function a Constructor, even if it leaves this alone.

There are very good tutorials out there on constructors, prototypes etc... Personally I learned a lot from Object Oriented Programming in JavaScript. It shows the equivalence of a function which 'inherits' its prototype, but uses this to fill in a new object's properties, and a function object that uses a specific prototype:

function newA() { this.prop1 = "one"; } // constructs a function object called newA
function newA_Too() {} // constructs a function object called newA_Too
newA_Too.prototype.prop1 = "one";

var A1 = new newA();
var A2 = new newA_Too();
// here A1 equals A2.
xtofl
  • 40,723
  • 12
  • 105
  • 192
1

First, JavaScript doesn't behave the same way about objects as C++/Java does, so you need to throw those sorts of ideas out of the window to be able to understand how javascript works.

When this line executes:

var myFunctionVar = new myFunction();

then the this inside of myFunction() refers to this new object you are creating - myFunctionVar. Thus this line of code:

 this.myProperty = "Am I an object!";

essentially has the result of

 myFunctionVar.myProperty = "Am I an object!";

It might help you to take a look at some documentation on the new operator. In JS, the new operator essentially allows you to create an object out of a function - any plain old function. There is nothing special about the function that you use with the new operator that marks it as a constructor, as it would be in C++ or Java. As the documentation says:

Creating a user-defined object type requires two steps:

  1. Define the object type by writing a function.
  2. Create an instance of the object with new.

So what you have done with the code

function myFunction(){
    this.myProperty = "Am I an object!";
}

is to create a function that would be useful as a constructor. The reason why the code myFunction.myProperty fails is that there is no reference named myFunction.

matt b
  • 138,234
  • 66
  • 282
  • 345
0

JavaScript is based on the ECMA script. Its specification uses the prototyping model for it to be OOP. How ever, ECMA script does not enforce strict data types. The object needs to be instantiated for the same reason that ECMA script requires a 'new' call which will allocate memory for the property, Otherwise it will remain a function and you can call it if you like, in which case, the property will initialize and then be destroyed when the function ends.

fasih.rana
  • 1,645
  • 1
  • 14
  • 27
0

Only when you instantiate with the new keyword does the function act as a constructor.

The result is an object that can use the "this" keyword to access member properties. The this keyword in the method does not make any sense when the function is used any other way.

Kekoa
  • 27,892
  • 14
  • 72
  • 91
  • hmm ... `javascript:newobj=function(){return {}}(); alert(newobj) ` creates `[object Object]` ... where's the gnu (as opposed to beef)? – Ekim Aug 18 '11 at 03:40
  • @Ekim I don't see how your demonstration relates. A function can return an object is what you've shown? If you did a `new newobj()` that might make it interesting to talk about. You are just returning an object, not creating your own type. – Kekoa Sep 01 '11 at 22:50