Is this a good way to create a class with a constructor and namespace in object literal style?
// root namespace
var myApp = myApp || {};
// sub namespace
myApp.model = {
// Constructor
Person: function (name) {
this.name = name;
}
};
myApp.model.Person.prototype = {
sayName: function () {
alert(this.name);
},
sayHi: function () {
alert("Hi, " + this.name);
}
};
var p1 = new myApp.model.Person("CK");
p1.sayName();
p1.sayHi();
JSHint shows no error and the full source code is here.
4 Answers 4
This looks good to me. From a once over;
- Consider using
'use strict'
- You cannot have 'private' functions or properties, that should be fine
- Naming is fine. As I personally dislike namespaces I would probably go for a 1 character namespace for
myApp
. - Indenting is fine
- 0 comments, you might want to consider one-liner comments separating the different sections of your code.
- Somewhat to krillgar's point,
instanceof
checks will have to check againstmyApp.model.Person
with your approach, this should be fine as well
All in all, JavaScript is not Java. I would not go so far as to say that namespaces are not idiomatic, but I would invite you read this.
-
\$\begingroup\$ If the listed code is under Person.js file, I would like to add another class
myApp.model.Facility
at another Facility.js file. How should I define the sub namespace block? This may worth for another question, but hope you can answer as well. \$\endgroup\$Lee Chee Kiam– Lee Chee Kiam2014年09月09日 13:32:10 +00:00Commented Sep 9, 2014 at 13:32 -
1\$\begingroup\$ @CK by combining your first line with my answer, ie
myApp.model = myApp.model || {};
followed bymyApp.model.Person = /*...*/
rather thanmyApp.model = { Person /*...*/
\$\endgroup\$James Thorpe– James Thorpe2014年09月09日 13:36:22 +00:00Commented Sep 9, 2014 at 13:36 -
\$\begingroup\$ I disagree re: comments - comments should only be added to code that is hard to understand without the comment. Assuming that this code will be read by people who know javascript, no comments are needed in this code. \$\endgroup\$TV's Frank– TV's Frank2014年09月09日 14:48:58 +00:00Commented Sep 9, 2014 at 14:48
-
\$\begingroup\$ @TV'sFrank It's an opinion, I would invite you to look at CK Lee's answer, the comments make it more readable in my mind. \$\endgroup\$konijn– konijn2014年09月09日 14:54:11 +00:00Commented Sep 9, 2014 at 14:54
-
\$\begingroup\$ @CKLee You should look up "javascript module pattern". You'll find a pattern for isolating your new variables from the global namespace, and within that context you can safely alias your namespaces to something convenient like this:
var Person = myApp.model.Person
. \$\endgroup\$Keen– Keen2014年09月12日 19:47:07 +00:00Commented Sep 12, 2014 at 19:47
I would suggest splitting the addition of the methods as follows:
myApp.model.Person.prototype.sayName = function () {
alert(this.name);
};
myApp.model.Person.prototype.sayHi = function () {
alert("Hi, " + this.name);
};
This way you can define additional methods in different places (if needed), as you're not replacing the entire prototype in one go.
-
\$\begingroup\$ In Java, normally we define all methods under
public class Person { ... }
block in a same file. Should it be good to put all related methods in the same place in Javascript as well? \$\endgroup\$Lee Chee Kiam– Lee Chee Kiam2014年09月09日 14:03:06 +00:00Commented Sep 9, 2014 at 14:03 -
\$\begingroup\$ Yes, it would be normal to group them together for ease of maintenance, but nothing actually stopping you in the language from putting them wherever you want \$\endgroup\$James Thorpe– James Thorpe2014年09月09日 14:48:35 +00:00Commented Sep 9, 2014 at 14:48
-
1\$\begingroup\$ I might be wrong, but I don't understand how the OP's solution doesn't allow you to later on add extra functions to the prototype (something like this: jsfiddle.net/vj4ujz0L/6) \$\endgroup\$Shivan Dragon– Shivan Dragon2014年09月09日 16:30:50 +00:00Commented Sep 9, 2014 at 16:30
-
1\$\begingroup\$ ShivanDragon is right. But I'd still recommend following James Thorpe's advice and adding your new methods to the prototype that's already there, since that approach works nicely with inheritance models. Oh, and please never add methods to a single prototype at multiple different places (unless your project is very small or you have a very clear set of conventions to which everyone agrees). \$\endgroup\$Keen– Keen2014年09月09日 17:48:25 +00:00Commented Sep 9, 2014 at 17:48
-
\$\begingroup\$ @ShivanDragon Yes, you can add them to the prototype, so long as you do so after you've already done the original assignment. If you add each method individually, it can make refactoring easier as you don't need to ensure they're added after the initial assignment \$\endgroup\$James Thorpe– James Thorpe2014年09月10日 08:38:20 +00:00Commented Sep 10, 2014 at 8:38
Based on comment from konijin, here is the updated version
// root namespace
var myApp = myApp || {};
// sub namespace
myApp.model = myApp.model || {};
// constructor
myApp.model.Person = function (name) {
this.name = name;
}
myApp.model.Person.prototype = {
sayName: function () {
alert(this.name);
},
sayHi: function () {
alert("Hi, " + this.name);
}
};
var p1 = new myApp.model.Person("CK");
p1.sayName();
p1.sayHi();
alert(p1 instanceof myApp.model.Person); // true
alert(p1 instanceof Object); // true
alert(p1 instanceof Person); // Uncaught ReferenceError: Person is not defined
When you assign an object to a prototype it changes the prototypal inheritance of that object. The prototype of the object will no longer have the original constructor function and it will point to Object
instead!
This affects your code, as it is evidenced by adding the following line:
console.log(p1.constructor === Object); // returns: true
Two ways to correct this:
A. Assign the functions directly. Don't wrap them in an object. That would look like this:
myApp.model.Person.prototype.sayName = function(){
alert(this.name);
};
myApp.model.Person.prototype.sayHi = function(){
alert("Hi, " + this.name);
};
B. Reassign the constructor to your object. You can do that by adding a line of code into your object, like this:
myApp.model.Person.prototype = {
constructor: myApp.model.Person, // <========
sayName: function () {
alert(this.name);
},
sayHi: function () {
alert("Hi, " + this.name);
}
};
Which is the same as this:
myApp.model.Person.prototype.constructor = myApp.model.Person;
It's worth noting that restoring the constructor in this manner creates a property with enumerable
set to true
. Native constructors are not enumerable by default. So if you're using ES5, this might be the preferred way to restore the constructor:
Object.defineProperty(myApp.model.Person.prototype, 'constructor', {
enumerable: false,
value: myApp.model.Person
});
More reading on this: http://javascript.info/tutorial/constructor
Explore related questions
See similar questions with these tags.
myApp.model.Person.prototype
only add those functions to thePerson
objects that are a part of yourmyApp.model
, and not allPerson
objects? If that is intended, that's fine. I could just see that becoming an issue later on. Do you have a separatePerson
object? \$\endgroup\$myApp.model
as my package, the fully qualified class name I want to make ismyApp.model.Person
. This class shall have a constructor that take Stringname
as parameter, and have 2 methodssayName
andsayHi
. It is simple to be achieved in Java, but I am not sure how to make it right in Javascript. \$\endgroup\$Person
object. Why it could be an issue if I define my qualified class name asmyApp.model.Person
? \$\endgroup\$Person
constructor is stored, whether it'smyApp.model.Person
or anywhere else. AllPerson
instances which share that constructor will have access to the functions attached to the constructor'sprototype
. \$\endgroup\$