rickyspanish
rickyspanish

Reputation: 27

Javascript: Is there a scenario where one would prefer to declare an object property using prototype?

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

Answers (4)

Witiko
Witiko

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

Petr Skocik
Petr Skocik

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

T.J. Crowder
T.J. Crowder

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

Raffaele
Raffaele

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

Related Questions