3

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?

4 Answers 4

3

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();
Sign up to request clarification or add additional context in comments.

2 Comments

Good answer, Can you please clarify the sentence "those functions don't use any closed over variables from the constructor"? Could not understand it...
@chenop I mean a variable declared with var inside the constructor, or a parameter of the constructor.
2

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).

Comments

2

You are returning a new object in your constructor. So, you would not receive an instance of Person when calling new Person(...) rather than a dump object which has your defined properties.

Try to define your methods object as the prototype of your constructor:

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

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

Alternatively, if you want to keep your properties name and age private, your could define your methods in the constructor. Simply assign methods to this.. For private properties, you don't have to assign the properties itself to your instance:

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

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

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

But I would prefer the first example. In the second, each new instance will create new methods. In the first example, all your instances refers to a single prototype object which should use less memory.

Comments

0

Remove the surrounding return {} in your class and it should work.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.