1

I'm a bit confused as to the purpose of constructor functions in javascript. I know that creating a constructor function through use of the new keyword also creates a new instance of a global object. Is that all one can do with them?

Also, the prototype of the new instance is the same as the global object, so why not just use the global object as many times as you need to instead of creating an instance? Can one add properties to a new instance?

Also, what points about constructor functions am I missing? Any help understanding this would be much appreciated.

dopatraman
  • 13,416
  • 29
  • 90
  • 154

2 Answers2

3

"I know that creating a constructor function through use of the new keyword also creates a new instance of a global object."

That's not right. Invoking a function with new create a new object, but it's not global. It's an instance of that function, and will have that function referenced as its .constructor.

The prototype is simply the object that all the instances of the constructor function point to. It lets all your instances reference a common set of properties.

The prototype is simply the object to which all the instances created by the constructor function contain an implicit pointer. It lets all your instances reference a common set of properties.

"Can one add properties to a new instance?"

Yes, they will be added to the object that was created from the constructor and not to its prototype.

function MyConstructor( arg ) {
    // If you invoke this with "new", 
    //    then "this" will be a reference to the new instance that is returned

    this.instance_prop = arg;
}

MyConstructor.prototype.proto_prop = "baz";

var inst1 = new MyConstructor( "foo" );

var inst2 = new MyConstructor( "bar" );


console.log( inst1.instance_prop ); // foo
console.log( inst1.proto_prop );    // baz

console.log( inst2.instance_prop ); // bar
console.log( inst2.proto_prop );    // baz

EDIT: Here's an example that is a very simple DOM wrapper:

http://jsfiddle.net/CEVwa/

  // constructor
var DOMWrapper = function(id) {
       // initialize a count property for the instance created
    this.count = 0;

       // if a String was passed to the constructor, go head and call fetchById
    if (typeof id === 'string') {
        this.fetchById(id);
    }
};
   // fetches an element by the ID given, and calls the .setup() method
DOMWrapper.prototype.fetchById = function(the_id) {

       // store the element with this instance
    this.element = document.getElementById(the_id);
    if( this.element != null ) {
        this.setup();
    }
};
   // stores a function on the .listener property that is passed to
   //      addEventListener. When clicked, the counter is incremented, and 
   //      appended to the element.
DOMWrapper.prototype.setup = function() {
    if (this.element != null) {

           // retain a reference to this instance that the listener can use
        var wrapper = this;

             // store the listener with this instance
        this.listener = function() {
            wrapper.count++;
            wrapper.element.appendChild( 
                document.createTextNode( ' ' + wrapper.count )
            );

               // when count is 10, call the tear_down method for this instance
            if( wrapper.count === 10 ) {
                wrapper.tear_down();
            }
        };
        this.element.addEventListener('click', this.listener, false);
    } else {
        this.no_element();
    }
};
DOMWrapper.prototype.no_element = function() {
    alert("you must first fetch an element");
};
    // remove the listener that is stored on the .listener property
DOMWrapper.prototype.tear_down = function() {
    if (this.element != null) {
        if( typeof this.listener === 'function' ) {
            this.element.removeEventListener('click', this.listener );
        }
    } else {
        this.no_element();
    }
};
<div id="one">element number one</div>
<div id="two">element number two</div>
var wrapper1 = new DOMWrapper();  // constructor with no arguments
wrapper1.fetchById("one");        // you must fetch the element manually

var wrapper2 = new DOMWrapper( "two" ); // with a string, it will fetchById for you

The constructor places a separate count property initialized to 0 on each instance that is created from it.

If you pass the constructor a String, it will automatically call the fetchById that is on the prototype. Otherwise you can call fetchById() directly after the new instance is returned.

Upon a successful getElementById, the fetchById stores the element found on the element property of that instance and calls the setup method, which stores click event listener on the .listener property of the element, and adds uses it to add a click event listener to the element.

The listener just increments the counter for that instance, and appends the new count to the element.

Once the count reaches 10, the tear_down method is called to remove the listener.

user113716
  • 318,772
  • 63
  • 451
  • 440
  • **Pedantry:** It's an instance of _the prototype_ of that function. And it's not really an instance. It's merely an Object with the prototype of that function in it's prototype chain. There's really no such thing as _instance_ as everything is an object. Instances don't _point_ to a prototype. They have the prototype object in their property lookup chain. – Raynos Aug 02 '11 at 16:13
  • @Raynos: Mostly fair points, but it seems an odd distinction when you say *"instances don't point to a prototype"* but then *"They have the prototype object in their property lookup chain"*. Certainly in order to create the chain of lookups, the object created must somewhere be pointing to the prototype object. – user113716 Aug 02 '11 at 16:18
  • ...also, I guess I don't think it's necessarily bad to refer to it as an instance of the function considering `inst1 instanceof MyConstructor` is `true`. It's just not an instance in a traditional sense. – user113716 Aug 02 '11 at 16:20
  • @patrick_dw the distinction is that saying instances point to the prototype is saying that the instance is a single pointer to a prototype. It's _more_ then a pointer. This kind of nitpicking is also of very little value. You can't justify using the "instance" terminology just because there is a poorly named operator called `instanceof`. that's cheating! You also forgot to tell him that he should use `Object.create` :( – Raynos Aug 02 '11 at 16:21
  • @Raynos: Wouldn't you agree that among JavaScript developers it is commonly understood that an instance of a function refers to the object that is rendered when calling that function with `new` (aside from whether or not you think `new` should be used)? And I wasn't nitpicking, I was trying to understand the distinction you were making. – user113716 Aug 02 '11 at 16:29
  • @patrick_dw and Raynos--still, the practicality of constructor functions and instances is still a mystery to me. Can either of you give an example of a situation where creating an instance of an object is more practical than creating an entirely new object? – dopatraman Aug 02 '11 at 17:07
  • @patrick_dw-- also, by using obj.prototyp = {} can one redefine an object's prototype? I feel like if so, this could lead to some interesting tricks. – dopatraman Aug 02 '11 at 17:11
  • @codeninja: Imagine you want to work with a set of objects that share a common functionality via a set of methods. You could place those methods all on the `prototype` of the constructor, and the instances created from the constructor would automatically have those methods available as properties. Because the prototype object is shared among the instances, only one copy of each function is needed irrespective of how many instances you create. – user113716 Aug 02 '11 at 17:13
  • ...You can redefine it on the constructor, but there isn't a *standard* way to redefine it on the instance. It is permanent. Although there is a non-standard `__proto__` property available in some browsers that lets you redefine it. Also when you give the constructor a new prototype object, it has no effect on previously created instances. They still reference the original. – user113716 Aug 02 '11 at 17:15
  • @codeninja code re-usability. If you want multiple new objects create one factory function that creates object. A constructor is simply a factory. – Raynos Aug 02 '11 at 17:21
  • @Raynos--I'm having trouble visualizing this. Can you provide an example? – dopatraman Aug 02 '11 at 17:25
0

Constructor functions are the way Javascript enables prototypical inheritance.

The prototype of the new instance is not the global object - it is the constructor's prototype property.

function MyConstructor(){
    //set member variables of the instance with
    this.x = 17;
}

MyConstructor.prototype = {
    //stuff in here will be shared by all MyConstructor objects:
    foo: function(){
        //
    },
    bar = 17;
}

var x = new MyConstructor();
//x's prototype is the MyConstructor.prototype object.
//the "new" sets the prototype magically behind the scenes.
// x can now use all attributes (and methods) of its prototype as its own:
x.foo();
//this is how OO works in Javascript - there are no "classes"

//note that overwriting a variable that is defined on the prototype overwrites
// only on the instance instead;
x.bar = 42;

vay y = new MyConstructor();
y.bar; //is still 17
hugomg
  • 68,213
  • 24
  • 160
  • 246