Question

I am new to JavaScript and trying to get my head around object constructors vs prototype when defining the base object's properties and methods with defaults.

// base object
function Animal (nameArg, typeArg) {
    this.name = nameArg,
    this.type= typeArg,
    this.sayHello = function() {
        console.log("Hello my name is", this.name, " and I am an ", this.type);
    }
}
// Setting defaults
Animal.prototype.name = "Anonymous";
Animal.prototype.type = "Animal";

//instantiating an animal WITHOUT any arguments passed in
var cat = new Animal; // I am getting undefined when I try to access properties like name

//instantiating an animal WITH argguments passed in
var lion = new Animal("Jimmy", "Lion"); // works as expected
Was it helpful?

Solution 2

What you're doing wrong

You're setting it to undefined: it's a value in itself. You shouldn't be defining it at all for it to work:

// base object
function Animal (nameArg, typeArg) {
    if (nameArg) this.name = nameArg;
    if (typeArg) this.type = typeArg;
    // could be added to prototype, it's the same for each instance
    this.sayHello = function () { console.log("Hello my name is " + this.name + " and I am an " + this.type); }; // don't use commas here, use the + operator instead
}

// Setting defaults
Animal.prototype.name = "Anonymous";
Animal.prototype.type = "Animal";

var cat = new Animal(); // passing no arguments to constructor
cat.sayHello(); // now gives correct result ("Hello my name is Anonymous and I am an Animal")

//instantiating an animal WITH argguments passed in
var lion = new Animal("Jimmy", "Lion"); // always worked

Why this is the case

There's actually a difference between a variable set to undefined and a variable never defined, funnily enough - yet another of JavaScript's common quirks. To tell the difference between an undefined variable and one never defined, take this example:

var foo = { x: undefined };
console.log('x' in foo); // true, x has been defined as "undefined"
console.log('y' in foo); // false, foo.y has never been defined

Arguments in JavaScript default to undefined, similar to when variables are initially defined without setting a value to them or being explicitly set to undefined:

function test(arg)
{   console.log(arg); // undefined
    console.log(doesNotExist); // ReferenceError: doesNotExist is not defined
}
test(); // run and see

By assigning to it, JavaScript keeps a record of it being assigned to, and therefore doesn't look up the prototype chain to see whether the property exists or not (reference).

OTHER TIPS

No need to use the prototype if you're instantiating the function. Also, I would call the Animal constructor by adding brackets afterwards:

// base object
function Animal (nameArg, typeArg) {
    this.name = nameArg || "Anonymous";
    this.type= typeArg || "Animal";
    this.sayHello = function() {
        console.log("Hello my name is", this.name, " and I am an ", this.type);
    };
}

//instantiating an animal WITHOUT any arguments passed in
var cat = new Animal(); // <-- note the brackets

//instantiating an animal WITH argguments passed in
var lion = new Animal("Jimmy", "Lion");

The reason your version of the code isn't working is because, despite the fact that you're not passing any arguments, the construction method of Animal is setting the original variables as undefined, overriding the prototype defaults.

function Animal (args) {
    if(typeof args === 'object') {
        this.name = 'nameArg' in args ? args.nameArg : default_value,
        this.type = 'typeArg' in args ? args.typeArg : default_value,
    }
    else
    {
        this.name = default_value;
        this.name = default_value;
    }
}

new Animal({});
new Animal({nameArg: 'hehe'});
new Animal({typeArg: 'hehe'});
new Animal({nameArg: 'hehe', typeArg: 'hehe'});

Change your constructor to:

function Animal (nameArg, typeArg) {
    this.name = nameArg || this.name; // if nameArg is not defined take default instead
    this.type= typeArg || this.type; // if typeArg is not defined take default instead
    this.sayHello = function() {
        console.log("Hello my name is", this.name, " and I am an ", this.type);
    }
}

And then:

var cat = new Animal();

You set the defaults in the proper way, but you override them in your constructor. Try this:

function Animal (nameArg, typeArg) {
    if (nameArg != null) this.name = nameArg,
    if (typeArg != null) this.type = typeArg,
    this.sayHello = function() {
        console.log("Hello my name is", this.name, " and I am an ", this.type);
    }
}

Also, setting the sayHello method on the prototype is a big win, more than just using the prototype for defaults.

cat = new Animal() is basically cat = new Animal( undefined, undefined ), and these arguments are used as this.name and this.type. To not overwrite defaults from prototype just check if the arguments were provided:

function Animal (nameArg, typeArg) {
    if(nameArg) this.name = nameArg;
    if(typeArg) this.type= typeArg;
    this.sayHello = function() {
        console.log("Hello my name is", this.name, " and I am an ", this.type);
    }
}

http://jsfiddle.net/K6Wu8/

You can use || to set the default to the prototype values if undefined is passed in.

function Animal (nameArg, typeArg) {
    this.name = nameArg || this.name,
    this.type= typeArg || this.type,
    this.sayHello = function() {
        console.log("Hello my name is", this.name, " and I am an ", this.type);
    }
}
// Setting defaults
Animal.prototype.name = "Anonymous";
Animal.prototype.type = "Animal";

//instantiating an animal WITHOUT any arguments passed in
var cat = new Animal; // I am getting undefined when I try to access properties like name

//instantiating an animal WITH argguments passed in
var lion = new Animal("Jimmy", "Lion"); // works as expected

Example Fiddle - http://jsfiddle.net/LwsRE/

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top