Why In JavaScript Is A Function Considered Both A Constructor And An Object?
Solution 1:
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:
- 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
andundefined
are special objects. Any attempt to use a method on these objects or define new methods causes an exception.
- 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 thenew
operator. The latter has the innate knowledge on how to use theprototype
property of the constructor, construct an object with proper internal links to the prototype, and call the constructor function on it setting upthis
correctly.
- Numbers have a special way with arithmetic operators:
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
:
- New empty object (
{}
) was created. - 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. 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 ofA
with the prototype and all, whiley
is our "naked" object we returned from the constructor.
Solution 2:
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
Solution 3:
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.
Solution 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.
Solution 5:
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.
Post a Comment for "Why In JavaScript Is A Function Considered Both A Constructor And An Object?"