Simulation Java and C++ classes in JavaScript

Hi,

Sorry for my English in advance.

I like Java and C++. I want to programming in JavaScript too. I need a help to rewrite Person class from Java:


public class Person
{

    Person( String nickName,
            String firstName,
            String lastName,
            String phoneNumber,
            String email )
    {
        m_nickName = nickName;
        m_firstName = firstName;
        m_lastName = lastName;
        m_phoneNumber = phoneNumber;
        m_email = email;
    }

    public Person()
    {
        this( "", "", "", "", "" );
    }

    public void setNickName( String nickName )
    {
        m_nickName = nickName;
    }

    public void setFirstName( String firstName )
    {
        m_firstName = firstName;
    }

    public void setLastName( String lastName )
    {
        m_lastName = lastName;
    }

    public void setPhoneNumber( String phoneNumber )
    {
        m_phoneNumber = phoneNumber;
    }

    public void setEmail( String email )
    {
        m_email = email;
    }

    public String getNickName()
    {
        return m_nickName;
    }

    public String getFirstName()
    {
        return m_firstName;
    }

    public String getLastName()
    {
        return m_lastName;
    }

    public String getPhoneNumber()
    {
        return m_phoneNumber;
    }

    public String getEmail()
    {
        return m_email;
    }

    private String m_nickName;
    private String m_firstName;
    private String m_lastName;
    private String m_phoneNumber;
    private String m_email;
}

I’ve created Person.js file and I’ve written the code:


function Person( nickName, firstName, lastName, phoneNumber, email ) {
    this.nickName = nickName;
    this.firstName = firstName;
    this.lastName = lastName;
    this.phoneNumber = phoneNumber;
    this.email = email;
}

But I don’t know how to write the rest thinks. Please, explain me this simple example!

A little out of my depth discussing the ins and outs of Javascript object orientated programming versus classes etc.

There are plenty of tutorials and books on the topic. For instance ‘The Principles of Object-Oriented JavaScript’, which is aimed specifically at those coming from another language. ‘Javascript Patterns’ is decent, and ‘Javascript the Good Parts’ albeit an old book comes highly recommended.

Note it is also worth looking into the ECMAScript 5 Object methods, such as Object.create, Object.defineProperty etc. These methods enable you to define whether a property is writable, enumerable or configurable.

Anyway here’s my stab at it. I’ve made use of the module pattern(I think it’s also referred to as a singleton). This enables me to create private variables by utilising ‘closures’.

// Using a self-invocating function to wrap the constructor up in it's own namespace.
// (function(x,y){........}(x,y))
Person = (function(){

    // Private local variables
    // The underscore is just for clarity and is optional.
	
    var _nickName,
        _firstName;
   
    var Person = function(nickName, firstName){
    
        _nickName = nickName;
        _firstName = firstName;
    
    };
    
    Person.prototype.setNickName = function (name) { _nickName = name;  };
    
    Person.prototype.getNickName = function () { return _nickName; };
    
    Person.prototype.setFirstName = function (name) { _firstName = name;  };
    
    Person.prototype.getFirstName = function () { return _firstName; };
    
    // Person and it's prototype methods have access to the private variables _nickName and _firstName
    // via their scope chain. The scope chain works from child to ancestor.
    // Returning the Person constructor to the global/outer Person forms a closure.
    // The Person constructor and prototypal methods will have permanent access to the private variables.
    
    return Person;  

}());

var robert = new Person ('Bob', 'Robert');

console.log(robert.getNickName()); // --> Bob

robert.setNickName('Bobby');

console.log(robert.getNickName()); // --> Bobby

console.log(_nickName); // -->ReferenceError: _nickName is not defined

A few alternatives

Person = (function(){
    
    var _nickName,
        _firstName;
   
    var Person = function (nickName, firstName) {
    
        _nickName = nickName;
        _firstName = firstName;
    
    };
    
    // Using an object literal instead
    Person.prototype = {
    
        constructor : Person, // Good idea to manually set the constructor
    
        setNickName : function (name) { _nickName = name;  },
        
        setFirstName : function (name) { _firstName = name;  },
        
        getNickName : function () { return _nickName; },
    
        getFirstName : function () { return _firstName; }
    
    };

    return Person; // return to the global. 

}());

//or

(function(){
    
    var _nickName,
        _firstName;
    
    var Person = function (nickName, firstName) {
    
        _nickName = nickName;
        _firstName = firstName;
    
    };
    
    Person.prototype = {
    
        constructor : Person,
    
        setNickName : function (name) { _nickName = name;  },
        
        setFirstName : function (name) { _firstName = name;  },
        
        getNickName : function () { return _nickName; },
    
        getFirstName : function () { return _firstName; }
    
    };
    
    // assign Person to a global Person.
    window.Person = Person;

}());

RLM2008 thank you! Could you help me again?

I cannot call methods of the class Person from the file “PersonTest.js”

This is my project:

Person.js



Person = ( function()
{
    var Person = function( nickName, firstName, lastName, phoneNumber, email )
    {
        m_nickName = nickName;
        m_firstName = firstName;
        m_lastName = lastName;
        m_phoneNumber = phoneNumber;
        m_email = email;
    };

    Person.prototype.setNickName = function( nickName ) {
        m_nickName = nickName;
    };

    Person.prototype.setFirstName = function( firstName ) {
        m_firstName = firstName;
    };

    Person.prototype.setLastName = function( lastName ) {
        m_lastName = lastName;
    };

    Person.prototype.setPhoneNumber = function( phoneNumber ) {
        m_phoneNumber = phoneNumber;
    };

    Person.prototype.setEmail = function( email ) {
        m_email = email;
    };

    Person.prototype.getNickName = function() {
        return m_nickName;
    };

    Person.prototype.getFirstName = function() {
        return m_firstName;
    };

    Person.prototype.getLastName = function() {
        return m_lastName;
    };

    Person.prototype.getPhoneNumer = function() {
        return m_phoneNumber;
    };

    Person.prototype.getEmail = function() {
        return m_email;
    };

//    Person.prototype = {
//        constructor: Person,
//        setNickName: function( nickName ) {
//            m_nickName = nickName;
//        },
//        setFirstName: function( firstName ) {
//            m_firstName = firstName;
//        },
//        setLastName: function( lastName ) {
//            m_lastName = lastName;
//        },
//        setPhoneNumber: function( phoneNumber ) {
//            m_phoneNumber = phoneNumber;
//        },
//        setEmail: function( email ) {
//            m_email = email;
//        },
//        getNickName: function() {
//            return m_nickName;
//        },
//        getFirstName: function() {
//            return m_firstName;
//        },
//        getLastName: function() {
//            return m_lastName;
//        },
//        getPhoneNumer: function() {
//            return m_phoneNumber;
//        },
//        getEmail: function() {
//            return m_email;
//        }
//    };

    var m_nickName;
    var m_firstName;
    var m_lastName;
    var m_phoneNumber;
    var m_email;

    return Person;
}() );

PersonTest.js



test( "setNickNameTest", function()
{
    var nickName = "8Observer8";
    var person = new Person( "", "", "", "", "" );
    person.setFirstName( nickName );
    
    var expected = "8Observer8";
    var actual = person.getNickName();
    equal( actual, expected );
} );

test( "setFirstNameTest", function()
{
    var nickName = "Ivan";
    var person = new Person( "", "", "", "", "" );
    person.setFirstName( nickName );
    
    var expected = "Ivan";
    var actual = person.getNickName();
    equal( actual, expected );
} );

index.php


<!DOCTYPE html>
<!--
To change this license header, choose License Headers in Project Properties.
To change this template file, choose Tools | Templates
and open the template in the editor.
-->
<html>
    <head>
        <meta charset="UTF-8">
        <title>Check Person Data</title>
        <link rel="stylesheet" href="css/qunit-1.12.0.css" />
        <script src="js/libs/qunit-1.12.0.js"></script>
        <script src="js/Tests/PersonTest.js"></script>
    </head>
    <body>
        <div id="qunit"></div>
        <div id="qunit-fixture"></div>
<!--        <table border="1">
            <tbody>
                <tr>
                    <td>Nick Name:</td>
                    <td><input type="text" name="nickName" value="" /></td>
                </tr>
                <tr>
                    <td>First Name:</td>
                    <td><input type="text" name="First Name" value="" /></td>
                </tr>
                <tr>
                    <td>Last Name:</td>
                    <td><input type="text" name="Last Name" value="" /></td>
                </tr>
                <tr>
                    <td>Phone Number:</td>
                    <td><input type="text" name="phoneNumber" value="" /></td>
                </tr>
                <tr>
                    <td>Email:</td>
                    <td><input type="text" name="email" value="" /></td>
                </tr>
            </tbody>
        </table>
        <input id="okButton" type="submit" value="Ok" />-->
    </body>
</html>

I need to get back to you regards the above. It’s flawed. Any subsequent new Persons will share the same private variables and overwrite them.

I don’t understand :frowning:

I don’t understand

This is a whole can of worms to explain.

My mistake was that all new Persons are sharing the one closure and therefore the same private variables.

My advice would be to read up on javascript functions. Specifically ‘closures’,‘variable objects’ and the ‘scope chain’.

Note it is a bit too involved for me to go through in detail here and I fear I may well just just confuse matters.

That said here is a rough explanation going by memory.

When a function is invoked it creates a variable object. It also creates a scope pointer which points to an outer function’s variable object. That’s if there is one. This scope chain carries on all the way down to the global object(window).

So if your function looks for variable x and can’t find it. It will look in the outer function and so on. It only works one way child -> ancestor.

A nightmare to explain but something like this.

var x;

function a(){
    // variable object a{ var y }.scope --> global{ var x }
    var y;
    
    function b(){
       // variable object b{ var z }.scope --> a{ var y }.scope --> global{ var x }
        var z;
    }

}

If you return function ‘b’ to outside of ‘a’ you form a closure. function ‘a’ cannot throw out it’s variable object for garbage collection as ‘b’ still has a pointer to it. It’s still in use.

Example

var closure = (function a(){
    // variable object a{ var fruit }.scope --> global[var closure]
    var fruit = 'apple';
    
    return function b(){
       // variable object b{ }.scope --> a{ var fruit }.scope --> global[var closure]
       console.log(fruit);
    }

}()); // invoke immediately so that closure is assigned the returned function b

// quick test on closure to confirm this

console.log(/[\\S\\s]*/.exec(closure)[0]);
/*--> function b(){
       // variable object b{ }.scope --> a{ var fruit }.scope --> global[var closure]
       console.log(fruit);
    }*/

// executing closure shows that it still has access to var fruit from function a.
closure(); //-->  'apple'

It’s all possibly a bit confusing from what I have shown here. There is more going on and it is worth reading up on.

Back to your problem though.

To use private variables the way we want them we need to put the methods and variables inside the constructor.

Like so.

var Person = function(nickName, firstName){

    // No need to declare variables nickName and firstName
    // Already delacred in our arguments(nickName, firstName)
    
    this.setNickName = function (name) { nickName = name; };
        
    this.setFirstName = function (name) { firstName = name; };
        
    this.getNickName = function () { return nickName; };
    
    this.getFirstName = function () { return firstName; };

};

Tests

var robert = new Person ('Bob', 'Robert');

console.log(robert.getNickName()); // --> Bob

robert.setNickName('Bobby');

console.log(robert.getNickName()); // --> Bobby

var william = new Person ('Billy', 'William');

console.log(robert.getNickName()); //--> Bobby
console.log(william.getNickName()); //--> Billy

It’s unfortunate as ideally you want your methods on the prototype, but it seems we are limited to this approach.

To quote from ‘The principles of OO Javascript’.

As discussed in Chapter 4 , placing methods on an object instance is less efficient than doing so on the prototype, but this is the only approach possible when you want private, instance-specific data.

So that’s about it. Really you need to do some research and read up on it. There are decent books out there. You also do need to get away from the idea of ‘classes’. That’s not Javascript. Prototypal inheritence is what you want to look into.

Well that’s my opinion:)

Just thinking about this. Here is probably a better alternative. You could use a factory instead.

var Person = function(nickName, firstName){

    var _nickName = nickName,
        _firstName = firstName

    var PersonConstructor = function(){};
    
    PersonConstructor.prototype = {
    
        constructor : PersonConstructor,
    
        setNickName : function (name) { _nickName = name; },
        
        setFirstName : function (name) { _firstName = name; },
        
        getNickName : function () { return _nickName; },
    
        getFirstName : function () { return _firstName; }
    
    };
    
    // returns the new object
    return new PersonConstructor(); 
    
};

//call the factory Person without 'new'
var robert = Person ('Bob', 'Robert');

console.log(robert.getNickName()); // --> Bob

robert.setNickName('Bobby');

console.log(robert.getNickName()); // --> Bobby

var william = Person ('Billy', 'William');

console.log(robert.getNickName()); //--> Bobby
console.log(william.getNickName()); //--> Billy

Hi, 8Observer8. The reason this seems complicated is because JavaScript doesn’t have classes, strictly speaking, so we have to devise some patterns to make things work.

In Java, a class definition doesn’t consume any memory. It’s not an object yet. It’s a blueprint for eventually creating an object. But in JavaScript, there are no classes, no blueprints. Instead, we’re going to create a concrete, memory-consuming object, and we’re going to treat this object as if it were a prototype – that is, a model from which copies will be spawned.

var PersonPrototype = {
    setFirstName: function (firstName) {
        this.firstName = firstName;
    },

    getFirstName: function () {
        return this.firstName;
    },
};

Here we’ve defined just a plain object with properties. Since this is a real, concrete object, we could start using it if we wanted – PersonPrototype.setFirstName(‘John’) – but we’re not going to do that. We want to keep this object pristine, because we’re going to use it as a model from which we create other objects.

var john = Object.create(PersonPrototype);
john.setFirstName('John');

console.log(john.getFirstName());

Object.create is how we spawn new copies from a prototypical instance. Though, “copy” may be too strong of a word. What actually happens is the object “john” is empty and blank except for one hidden property that points to the object “PersonPrototype”. When we access a property, such as “setFirstName”, JavaScript checks if that property is defined on the “john” object, and if not, then JavaScript follows the prototype link in this case to the “PersonPrototype” object and continues searching for that property. And of course it will indeed find “setFirstName” on the “PersonPrototype” object. This is how JavaScript implements inheritance. Every new “copy” we make of “PersonPrototype” will be blank, but they’ll have that hidden prototype link that allows them to share the behavior that was defined in “PersonPrototype”.

This behavior, all by itself, is already enough to create complex inheritance hierarchies.

var PersonPrototype = {
    setFirstName: function (firstName) {
        this.firstName = firstName;
    },

    getFirstName: function () {
        return this.firstName;
    },
};

var EmployeePrototype = Object.create(PersonPrototype);

EmployeePrototype.setJobTitle = function (jobTitle) {
    this.jobTitle = jobTitle;
};

EmployeePrototype.getJobTitle = function () {
    return this.jobTitle;
};

var jane = Object.create(EmployeePrototype);
jane.setFirstName('Jane');
jane.setJobTitle('Web Dev');

console.log(jane.getFirstName());
console.log(jane.getJobTitle());

But we’re not done yet. Back when JavaScript was first being created, people thought that prototypal inheritance would seem too foreign to people coming from classical (class-based) inheritance languages, such as Java or C++. So they added some syntactic sugar.

When you define a function – any function – it’s created with a property called “prototype”, whose value is a plain object.

function anyRandomFunction() {
}

// every function gets this prototype property, an object, automatically
console.log(anyRandomFunction.prototype);

The function’s prototype object is meant to serve the purpose as our earlier objects that I suffixed with the word “Prototype”, and the function itself serves as the constructor – that is, the function itself will do any initialization work on all newly created objects. And the way you create objects in this setup is by using the “new” keyword with a function.

// This is the constructor function
// Use it to do any initialization on newly created objects
function Person() {
    // Set a default name
    this.firstName = 'John'
}

// Add functions (methods) to the function's prototype object
Person.prototype.setFirstName = function (firstName) {
    this.firstName = firstName;
};

Person.prototype.getFirstName = function () {
    return this.firstName;
};

var harry = new Person();
harry.setFirstName('Harry');

Note that saying new Person() is equivalent to doing:

// Spawn new copy from a prototypical instance
var harry = Object.create(Person.prototype);

// Do any initialization work
// Invoke the constructor, using "harry" as the value for "this"
Person.call(harry);

// Now use it like normal
harry.setFirstName('Harry');

There’s still more to learn than what I’ve described here. JavaScript is flexible enough that there are alternative patterns to achieve similar behavior, and nuances to all of them. It’s complex just enough that there are libraries to make this work easier.

Probably clearer than my ramblings:)

RLM2008 and Jeff Mott, thank you very much to both. I will study your answers :slight_smile:

Jeff Mott, I have studied you answer but I met a problem. I created the project in NetBeans with following files:

In PersonPrototype.js I keep the prototype the object:



var PersonPrototype = {
    setFirstName: function( firstName )
    {
        this.firstName = firstName;
    },
    getFirstName: function()
    {
        return this.firstName;
    }
};

In “index.php” I run the “script.js” file:


<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script src="js/script.js"></script>
    </head>
    <body>
        <?php
        // put your code here
        ?>
    </body>
</html>

script.js



var ivan = Object.create( PersonPrototype );
ivan.setFirstName( 'Ivan' );

console.log( ivan.getFirstName() );

But I see the error:

Uncaught ReferenceError: PersonPrototype is not defined

I think it is because I need to include file “PersonPrototype.js” in the file “script.js”

Well, yes. You put the definition of PersonPrototype inside PersonPrototype.js, but then you never included that script in your page.

Yes, I understand! I need to include the file “PersonPrototype.js” in “index.php”. Thank you! I will study next :slight_smile:

I write my application with TDD methodology. I download the files from here: http://qunitjs.com/

  1. qunit-1.15.0.js http://code.jquery.com/qunit/qunit-1.15.0.js
  2. qunit-1.15.0.css http://code.jquery.com/qunit/qunit-1.15.0.css

My project tree:

index.php


<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script src="js/PersonPrototype.js"></script>
        <script src="js/script.js"></script>
    </head>
    <body>
        <!-- Tests -->
        <div id="qunit"></div>
        <div id="qunit-fixture"></div>
        <link rel="stylesheet" href="css/qunit-1.15.0.css" />
        <script src="js/libs/qunit-1.15.0.js"></script>
        <script src="tests/PersonPrototypeTest.js"></script>
    </body>
</html>

script.js


var john = Object.create(PersonPrototype);
john.setFirstName( 'John' );

console.log( john.getFirstName() );

PersonPrototype.js


var PersonPrototype = {
    setFirstName: function( firstName )
    {
        this.firstName = firstName;
    },
    getFirstName: function()
    {
        return this.firstName;
    }
};

PersonPrototypeTest.js



QUnit.test( "PersonPrototype. Nick Name Test", function( assert )
{
    var ivan = Object.create( PersonPrototype );
    var nickName = "8Observer8";
    ivan.setNickName( nickName );

    var actual = ivan.getNickName();
    var expected = "8Observer8";
    assert.equal( actual, expected );
});

QUnit.test( "PersonPrototype. First Name Test", function( assert )
{
    var ivan = Object.create( PersonPrototype );
    var firstName = "Ivan";
    ivan.setFirstName( firstName );

    var actual = ivan.getFirstName();
    var expected = "Ivan";
    assert.equal( actual, expected );
} );

QUnit.test( "PersonPrototype. Last Name Test", function( assert )
{
    var ivan = Object.create( PersonPrototype );
    var lastName = "Enzhaev";
    ivan.setLastName( lastName );
    
    var actual = ivan.getLastName();
    var expected = "Enzhaev";
    assert.equal( actual, expected );
});

The result of the tests:

I will comment “Tests” in “index.php” when I will give my web-applications to the customers :smiley:

Jeff Mott, your post will help Russian newbie too: programmersforum.ru/showpost.php?p=1403488&postcount=56