1

I'm learning to define 'class like' function on JavaScript using the prototype property. When I'm defining a People class like so, everything is working great:

var People = function(firstName, lastName){
    // public
    this.firstName = firstName;
    this.lastName  = lastName;

    this.fullName = function(){
        console.log(full_name);
        return this;
    };

    // private
    var self = this;
    var full_name = function(){
        return self.firstName + ' ' + self.lastName;
    }();
};

var person = new People('John', 'Smith');
person.fullName();  // => "John Smith"

However, when I'm moving the fullName method outside of the initial definition like so:

var People = function(firstName, lastName){
    // public
    this.firstName = firstName;
    this.lastName  = lastName;

    // private
    var self = this;
    var full_name = function(){
        return self.firstName + ' ' + self.lastName;
    }();
};

People.prototype.fullName = function(){
    console.log(full_name);
    return this;
};

var person = new People('John', 'Smith');
person.fullName();  // => "ReferenceError: full_name is not defined"

I'm getting an error. I can't understand why...??

Lior Elrom
  • 19,660
  • 16
  • 80
  • 92
  • 2
    There are no classes or private, it's functions and scope, but some smartypants decided to call it "classes" and "private / public" (It was probably Crockford, lets blame him). – adeneo May 01 '14 at 21:52
  • It's not clear what you want. If you want "full_name" to be a function visible as a property of the prototype, then there's no reason for it to be "private". – Pointy May 01 '14 at 21:52
  • @adeneo You're right... that's why I called it "class like" lol – Lior Elrom May 01 '14 at 22:13
  • 1
    see also [Javascript: Do I need to put this.var for every variable in an object?](http://stackoverflow.com/questions/13418669/javascript-do-i-need-to-put-this-var-for-every-variable-in-an-object) – Bergi May 01 '14 at 22:16
  • @Bergi Nicely explained but it's not what I asked here. – Lior Elrom May 01 '14 at 22:42

2 Answers2

3

Here:

var full_name = function(){
    return self.firstName + ' ' + self.lastName;
}();

you're setting the variable "full_name" to be the result of calling that anonymous function. It's just a variable declared in the context of that constructor, however, so that doesn't really matter; nothing outside of the constructor can access it.

Thus in that prototype function, you get an error because "full_name" is in fact not defined. If you want the full name function on the prototype, just declare it there:

People.prototype.fullName = function() {
  return this.firstName + " " + this.lastName;
};

If you want a "private" function that accessible only to code in the constructor, then you'd do something like this:

var People = function(firstName, lastName){
    // public
    this.firstName = firstName;
    this.lastName  = lastName;

    // private
    var self = this;
    function fullName(){
        return self.firstName + ' ' + self.lastName;
    }

    this.addressMe = function(msg) {
        return fullName() + ": " + msg;
    };
};

Then you can do:

var bob = new People("Bob", "Scum");
alert( bob.addressMe("stand and deliver") ); // "Bob Scum: stand and deliver"
Pointy
  • 405,095
  • 59
  • 585
  • 614
  • 1
    you're using the wrong pattern for private shared members. The pattern you're using is for private instance specific (unique for every instance). Since full_name is a function and can be shared you don't have to ditch prototype to implement it. – HMR May 02 '14 at 00:31
  • 1
    @HMR I agree; I wouldn't code up anything like this, probably. The intentions of the original question are not really clear. – Pointy May 02 '14 at 12:38
1

Pointy is using the wrong pattern for private shared members, the pattern is for private instance specific members and every instance will have it's own addressMe function.

The pattern for private shared members could wrap the prototype in an IIFE:

var People = function(firstName, lastName){
    // public
    this.firstName = firstName;
    this.lastName  = lastName;
};

(function(){
  // private shared member
  var full_name = function(){
      return this.firstName + ' ' + this.lastName;
  };
  //privileged member that has access to private shared
  //  fullName is a closure and full_name is available
  //  in the colosure scope created for this function
  People.prototype.fullName = function(){
    //calling private member, make sure you set
    //  the invoking object (this) by using
    //  Function.prototype.call
    console.log(full_name.call(this));
    return this;
  };
}());

var person = new People('John', 'Smith');
person.fullName();

You can't use this pattern for instance specific private members but the function full_name is not instance specific.

A pattern for protected and more info on prototype and constructor functions can be found here.

Community
  • 1
  • 1
HMR
  • 37,593
  • 24
  • 91
  • 160
  • Thx for ur answer but much more for the link you attached in the end of it. Great explanation there!!! Do you share a blog of ur own as well? – Lior Elrom May 02 '14 at 01:54
  • @Lior Thank you, don't have a blog. Think JavaScript prototype is one of the most misunderstood things in programming and wrote that while trying to figure it out myself. SO is perfect for that as I hope comments will point out mistakes or suggest improvements. – HMR May 02 '14 at 02:28