Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Beginner JavaScript Pattern

I'm recently started studying objects, closures, scopes and the such. I'm trying to implement my own code using these techniques but am running into issues.

function Person(name, age) {
    this.name = name;
    this.age  = age;

    return {
        ageUp: function ageUp() {
            this.age++;
        },
        printInfo: function printInfo() {
            console.log(this.name + " is " + this.age + " years old");
        },
        changeName: function changeName(newName) {
            this.name = newName;
        }
    }
}


var jeff = new Person('jeff', 28);
jeff.printInfo();

Oddly, this returns undefined is undefined years old. Does the printInfo property not have this.name in its scope because the returned object has no recollection of the function?

Also oddly enough, if I change all the instances of this.name and this.age to regular private variables (such as var personName = name;), somehow the returned object will operate properly and I can use the ageUp and printInfo as expected.

Am I combining two design patterns that I shouldn't be? What would be the best way to go about this?

like image 641
inthenameofmusik Avatar asked Dec 15 '25 20:12

inthenameofmusik


2 Answers

The problem is that when you do:

return { ...

You are creating a new object, separate from the object that the new keyword previously created (the one you assigned two properties to). You might notice that jeff instanceof Person is false and jeff.constructor === Object.

See this example:

function Person(name, age) {
    this.name = name;
    this.age  = age;

    return {
        theObjectThatNewCreated: this
    }
}

var jeff = new Person('jeff', 28);
console.log( JSON.stringify( jeff ) );
// {"theObjectThatNewCreated":{"name":"jeff","age":28}}
console.log( jeff.constructor );
// Object
console.log( jeff.theObjectThatNewCreated.constructor );
// Person

You could fix it by assigning the name and age properties to the object you return instead of to this:

function Person(name, age) {
    return {
        name: name,
        age: age,
        ageUp: function ageUp() {
            this.age++;
        },
        printInfo: function printInfo() {
            console.log(this.name + " is " + this.age + " years old");
        },
        changeName: function changeName(newName) {
            this.name = newName;
        }
    }
}


var jeff = new Person('jeff', 28);
jeff.printInfo();

But then Person isn't really a constructor, it is just an object factory and there is no point calling it with new. The objects it returns are not instances of Person, they are plain old Objects. There is a better way.

Am I combining two design patterns that I shouldn't be? What would be the best way to go about this?

I would say you are combining the revealing module pattern with a normal JavaScript constructor.

Instead of returning a new object you can just use this all the way through, to assign those functions as properties of the Person object instead of a new object:

function Person(name, age) {
    this.name = name;
    this.age  = age;

    this.ageUp = function ageUp() {
        this.age++;
    };

    this.printInfo = function printInfo() {
        console.log(this.name + " is " + this.age + " years old");
    };

    this.changeName = function changeName(newName) {
        this.name = newName;
    };
}


var jeff = new Person('jeff', 28);
jeff.printInfo();

But since those functions don't use any closed over variables from the constructor, they really should be added to the prototype of the constructor instead:

function Person(name, age) {
    this.name = name;
    this.age  = age;
}

Person.prototype.ageUp = function ageUp() {
    this.age++;
};

Person.prototype.printInfo = function printInfo() {
    console.log(this.name + " is " + this.age + " years old");
};

Person.prototype.changeName = function changeName(newName) {
    this.name = newName;
};

var jeff = new Person('jeff', 28);
jeff.printInfo();
like image 97
Paul Avatar answered Dec 17 '25 10:12

Paul


You are correct in that you are combining two patterns. Firstly, under normal circumstances, a constructor wouldn't return anything. (There are exceptions which I won't delve into here).

You probably want to do something like this:

function Person(name, age) {
    this.name = name;
    this.age  = age;
}

Person.prototype.printInfo = function() {
    console.log(this.name + " is " + this.age + " years old");
};

// and so on. Now you can say

var jeff = new Person('jeff', 28);
jeff.printInfo(); // => jeff is 28 years old

I am assuming you want those methods on the prototype so they are shared (it's not mandatory however).

like image 25
theharls Avatar answered Dec 17 '25 11:12

theharls



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!