Reputation: 27
I've just started to learn Javascript.
As per my understanding so far, you can declare class/static variables in two ways. The 1st way:
Blog.prototype.signature = "By Blogger-Name";
And the 2nd way:
Blog.signature = "By Blogger-Name";
By using the first method, the variable signature is available to all instances of Blog.
However, a variable that remains the same value for all instances of a class should really not be an instance variable. As I see it, class/static variables should be declared using the second method only.
Hence, my question is, is there a scenario where one would be required/forced to declare variables the first way? Or is my understanding of all of this lacking in any way? Please let me know.
EDIT: In what instances is the first method preferred and, similarly, in what instances is the second method preferred?
EDIT 2: So I've learnt that the first method actually adds instance properties with a default value. This can also be achieved by setting a default value in the constructor itself. So when would adding a property this way (i.e. using prototype) be preferred?
Upvotes: 0
Views: 95
Reputation: 3387
The only difference between foo1 = {}
and foo2 = new Bar
is the fact that foo1.__proto__ == Object.prototype
, whereas foo2.__proto__ == Bar.prototype
. In JavaScript there are no classes and no instances, only plan objects.
Now, if you set Bar.prototype.property = "value"
, then looking up foo2.property
first checks, whether foo2
has its own property called property
. If it does not, it continues with foo2.__proto__
until it finds an object that does have an own property called property
or until it gets to an object, whose __proto__
is null
(usually Object.prototype
).
It should now be obvious that by declaring Bar.prototype.property
, you are setting up the default value, which looking up foo2.property
resolves to, when it doesn't have its own property property
. Setting up foo2.property
in the constructor defines foo2
's own property. You could call it an instance variable, but really, it's just a hash map key.
Upvotes: 0
Reputation: 60068
I guess it all depends on how you want to access the signature variable.
If you want an easy access to it from a Blog instance, use the prototype
versions. Then you can do:
var blog = new Blog;
console.log(blog.signature);
Otherwise, you'll have to use Blog.signature
or blog.constructor.signature
.
I wouldn't worry about making signature
an instance variable
when you put it in the prototype because that's not what you're doing.
Blog.prototype
doesn't get copied around -- it's not something every instance would have its own private copy of. It's a shared object that blog
objects (=objects created with new Blog
) will have set as their __proto__
(=Object.getPrototypeOf(blog)
). __proto__
will be used to search for properties (that includes functions) that are not direct attributes of the instance.
A __proto__
can be overriden by setting an actual instance variable:
var Blog = function () {};
Blog.prototype.signature = "Original sig";
var blog = new Blog();
blog.signature === "Original sig"
//^looked up in Blog.prototype === Object.getPrototypeOf(blog) === blog.__proto__
blog.signature = "New sig"
//^set as an instance property
blog.signature === "New sig"
Blog.prototype.signature !== "New sig" //actual instance properties override prototype properties, but they don't over-write them
var blog2 = new Blog()
blog2.signature === "Original sig"
A classical object-oriented programming language such as C++/Java does a very similar thing, actually. Objects in C++/Java have properties and methods, and methods behave as if they were function pointer properties of instances, but they're not. Methods don't add any size to instances. They're all looked up in a shared place--your class's prototype if you will. The difference is, that in C++/Java the link doesn't exist at runtime (it's maintained by the compiler) and is only for methods. In JavaScript, each object has a real link to a class-shared lookup place (the object's constructor's prototype
attribute), the link is accessible via either the objet's __proto__
attribute or more standardly via what you get with Object.getPrototypeOf(someobject)
, and it's not only for methods (like in C++/Java) but for properties as well.
Upvotes: 0
Reputation: 1074355
Hence, my question is, is there a scenario where one would be required/forced to declare variables the first way?
The only thing that comes to mind would be if you needed to allow for code using an object created via new Blog
to access the property without knowing the constructor function's name. E.g.:
function useABlogInstance(blog) {
// Here, assume I don't know that `blog` was created via `new Blog`
console.log(blog.signature);
}
Although a canny user could do
console.log(blog.constructor.signature);
to access Blog.signature
(assuming you maintain the constructor
backlink), that wouldn't be very clean. :-)
It's important to note that a property on Blog.prototype
isn't a "class" or "static" property, it's an instance property (on the Blog.prototype
object). While it's true that reading that property from an object created via new Blog
will find it on the prototype (if the instance doesn't have its own signature
property), the results of writing to the property are very, very different. Consider:
function A() {}
A.prototype.prop = "prototype prop";
var a1 = new A();
var a2 = new A();
snippet.log(a1.prop); // "prototype prop"
snippet.log(a2.prop); // "prototype prop"
a1.prop = "updated";
snippet.log(a1.prop); // "updated" -- because `a1` has its own `prop` property
// now and no longer uses the prototype's
snippet.log(a2.prop); // "prototype prop"
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
Contrast that with:
function B() {}
B.prop = "B prop";
var b1 = new B();
var b2 = new B();
snippet.log(b1.constructor.prop); // "B prop"
snippet.log(b2.constructor.prop); // "B prop"
snippet.log(B.prop); // "B prop"
b1.constructor.prop = "updated";
snippet.log(b1.constructor.prop); // "updated"
snippet.log(b2.constructor.prop); // "updated"
snippet.log(B.prop); // "updated"
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
For "class"-wide information, I would always use Blog.signature
, not Blog.prototype.signature
. I'd use Blog.prototype.signature
for instance-specific default properties where the values are primitives or functions (but not non-function objects or arrays, I'd set those up in the constructor function).
Side note: You're not declaring a variable in either of your code snippets. You're adding a property to an object. In the first snippet, you're adding the property to the object that will get assigned as the prototype of objects created via new Blog
. In the second case, you're adding a property to the Blog
object (functions are objects). Neither is a variable, in both cases it's a property.
Upvotes: 2
Reputation: 20885
There are no class variables in Javascript. Actually, there are no classes altogether in Javascript. Thinking in terms of classes and static variables (are you a Java dev?) won't let you know what's going on. Even if the concept of "a class" is independent of the programming language to some degree, it is important to understand that it's not a fundamental programming construct - and in fact there are languages without classes, like Javascript.
Javascript is a very simple language: it has functions and dictionaries, and has the syntax to combine both and support programming the object-oriented way. Take this example:
var johnSnow = { first: "John", last: "Snow" };
function fullName(character) {
return character.first + " " + character.last;
}
console.log(fullName(johnSnow));
And now the OO version:
var Character = function(first, last) {
this.first = first;
this.last = last;
}
Character.prototype.fullName = function() {
return this.first + " " + this.last;
}
console.log(new Character("John", "Snow").fullName());
Finally we can answer your question: when should you set a property on a constructor and when you should use an instance?
Set the property on the instance when they should not be shared with other instances (obviously). You recognize these properties because often are used in instance methods and read with this.$name
Set the property on the constructor... never! Properties set on constructors are simply global variables. So just use global variables, possibly namespacing them (but obviously you'll need a global at some point!)
Upvotes: 1