ES6 class / instance properties - javascript

This is going to be a relatively long question, but one I would really like to understand. Final question formulated at the bottom of the question.
I have read the answers to this question:
ES6 class variable alternatives
The question on why this is not accepted syntax in ES6:
class MyClass {
const MY_CONST = 'string';
constructor(){
this.MY_CONST;
}
}
1) The first answer mentions:
Remember, a class definition defines prototype methods - defining
variables on the prototype is generally not something you do.
I don't get this; static variables in a class based language would appear to serve the same purpose as properties defined on the prototype in JS.
Obviously not an instance variable like a person's name, but it could be a default MAX_SPEED for a vehicle, or a counter that is shared by all instances. If the instance doesn't override the prototype's MAX_SPEED, it returns to the default. Isn't that the exact purpose of a static variable?
2) The following post (ES6 spec proposal) formulates:
There is (intentionally) no direct declarative way to define either
prototype data properties (other than methods) class properties, or
instance property.
Class properties and prototype data properties need be created outside
the declaration.
I don't see the actual difference in declaring / initialising an instance/class variable with default value within the class itself (outside the constructor)? What does it matter if it's on the prototype? If it concerns an instance variable with a default value that will be likely for all instances (yet overridable), I don't see what is the problem. So what is this intention about exactly?
3) The second answer on the question ES6 class variable alternatives confuses me (though not from a technical pov).
From within a class method that variable can be accessed as
this.constructor.foo (or MyClass.foo).
These class properties would not usually be accessible from to the
class instance. i.e. MyClass.foo gives 'bar' but new MyClass().foo is
undefined
This indicates that it is clearly possible to declare a class variable on the class (or underlying function) as implemented in this example: http://www.es6fiddle.net/iehn0hxp/
class Car{
constructor(){
//Set instance variable
this.instance_var = 220;
//Set class variable
this.constructor.class_var = 240;
}
}
var Mercedes = new Car();
var Audi = new Car();
//Instance property
console.log(Mercedes.instance_var); //220
//Class property
console.log(Car.class_var); //240
//Set instance property
Mercedes.instance_var = 120; //Well I don't know really :-)
console.log(Mercedes.instance_var); //120
//Class property
Car.class_var = 140;
console.log(Car.class_var); //140
//Can be accessed from the constructor property on the instance
console.log(Mercedes.constructor.class_var); //140
console.log(Audi.constructor.class_var); //140
So in the end it is possible to declare a static property from within the class; so I don't see what is the difference declaring it within the constructor, versus just defining it on the class, vs defining it from outside? In the end it just seems to be a trivial technical modification to put it in the constructor vs as an actual class definition (the result will be the same).
Is it really just a design choice to only make methods available?
Ultimate question:
Because I don't understand how being a prototype-language changes the
philosophy of having properties on the prototype against
static variables on a class. It looks the same to me.
I hope that my question is clear, shout if not.

I don't get this; static variables in a class based language would appear to serve the same purpose as properties defined on the prototype in JS.
No, static variables are more like properties defined on the constructor. Variables on the prototype would be closer to instance variables, but they’re not nearly as useful because they’re shared between instances. (So if you modify a mutable property on the prototype, it will be reflected in all other instances of that type.)
This also answers your other questions, I think, but to recap:
variables on the prototype are not like static variables in that they appear to belong to every instance rather than just the class
variables on the prototype are not like instance variables in that each instance of the class doesn’t have its own instance of the variable
therefore, variables on the prototype are not that useful and they should be assigned to in the constructor (instance variables) or assigned to the constructor (class variables)
they’re also properties, not variables
And a non-ES6-sugared example:
function Something() {
this.instanceProperty = 5;
}
Something.staticProperty = 32;
Something.prototype.prototypeProperty = 977;
var s = new Something();
console.log(s.instanceProperty); // 5
console.log(s.prototypeProperty); // 977? If you want a class property,
// this is not what you want
console.log(s.staticProperty); // undefined; it’s not on the instance
console.log(Something.staticProperty); // 32; rather, it’s on the class
console.log(Something.prototypeProperty); // undefined; this one isn’t

Related

In a javascript Class declaration, which is the diference between declaring a method inside the constructor and the body? [duplicate]

This question already has answers here:
Use of 'prototype' vs. 'this' in JavaScript?
(15 answers)
Closed 3 years ago.
The following 2 snippets give same result. I wondered what is the real diffence in these 2 approaches and when should one be used instead of another way. Can someone help me out understanding the difference?
Case 1:
class Person{
constructor(name){
this.name = name;
}
printData(){
console.log(this.name);
}
}
Case 2 :
class Person{
constructor(name){
this.name = name;
this.printData = function(){
console.log(this.name);
}
}
}
The different is instance property versus prototype.
When you define a function inside the constructor, each new instance gets a new function defined, akin to
{
printData: function () { ... }
}
When you define the method in the class, it is defined in the prototype and shared by every instance.
i.e. with first approach,
const p1 = new Person('bob')
const p2 = new Person('rob')
p1.printData === p2.printData // false
with second approach
const p1 = new Person('bob')
const p2 = new Person('rob')
p1.printData === p2.printData // true
There are a few differences but the most important in my opinion is that when you add a class method, TypeScript will transpile it to be a so-called prototype method, which is kind of shared for all object instances. On the other hand, adding the method to the object in the constructor will add it only to that object instance, possibly overwriting any prototype method with the same name.
Each scenario might have its use-cases, but in my experience, most of the times you should prefer the prototype methods, so in TypeScript, class methods. Also it fits more into TypeScript's OO world.
You can read more about prototype members versus object members for example here.
Another difference is that the Class 2 example might not even be valid TypeScript code (depending on version I guess), because basically you're dynamically adding the method to the instance, and not statically declaring it in the class. Even if you can make it valid TypeScript (e.g. using hacks like casting to any), I'm pretty sure you will loose for example editor intellisense for the method printData.

Can I seperate instantiated vs. referenced variables in Javascript/Typescript?

Is there way to tell if a instance variable was instantiated inside of the class/object or is referenced from outside?
class A {
b1 = new B();
constructor(public b2:B) {}
}
I.e. I want to find the object properties / variables that are "owned" by the object (i.e. instantiated by itself, here b1) vs. the ones that are referenced (here b2).
Your code compiles to this in JS (I added the class def for B):
class B {
}
class A {
constructor(b2) {
this.b2 = b2;
this.b1 = new B();
}
}
Which is what you have to work with unless you use a reflection library, or add annotations. So there is no API that I know of to simply output parameter names for the constructor.
If you still want to proceed and don't want to invest heavily, a cheap version of this may just be using some clever-enough regex on the output from A.toString() to detect parameter names and correlate them with member names on an instance of A.
No, there is no way to do that.
Notice that the distinction is even more blurry than you might think: a constructor argument value might have been created solely for that instance, to be owned by it (new A(new B())), or an object that was created within the constructor of A might later become shared after it has been accessed from outside (new C(a.b1)). If you want to decide whether an object is "owned" by your A instances or not, you need to describe that contract in the documentation of your class (i.e. for its constructor, and the methods/accessors that might return a field).
If we need smth owned it means it should be encapsulated and not available for public use. My proposition for you is to explicitly say that b1 is a private field. But in terms of which property was initialized in the constructor itself and which was at the field definition level is unknown.

Is there any difference between WeakMap and private member of a class in JavaScript ES6?

I am trying to figure out what is different between defining local/private properties and methods inside a class compared to WeakMap constructor?
is the purpose of hiding them from outside are the same or is there other thing to use them?
Given your current implementation:
Your let x isn't very useful, because it can only be referenced in the constructor. It can't be used in any of the prototype methods. The WeakMap, on the other hand, can be referenced anywhere. That's a pretty big difference.
Your WeakMap can be referenced anywhere. This means that it's not actually providing any privacy; anything with a reference to an instance and the WeakMap may access the radius value that you wanted to be private.
If you wanted the WeakMap to be properly private, via closures, you could use an IIFE to define the class, eg:
const myClass = (() => {
const weakMap = new WeakMap();
return class Circle {
// etc
})();
That way, the top level only has a reference to the myClass, and not to the weakMap.
I would use your let x in the constructor whenever a variable is only needed in the constructor - not necessarily for the sake of hiding data, but just because it has no use outside of the constructor. See Why is it good programming practice to limit scope?
Note that there is currently a proposal for class fields which will probably eventually be implemented, which includes syntax for private fields, eg:
class Circle {
constructor(radius) {
this.#x = radius;
// ...
This will allow #x to be accessed from anywhere within Circle, but nowhere else. It's functionally equivalent to the WeakMap implementation.

Prototypal inheritance causes shared references

I'm want to understand how and why are prototypes useful in Javascript. After I thought I knew what was going on, I stumbled with the fact that a prototype is just an object and can't be "shared" by many objects in the way I thought. Let me elaborate with an example:
var SpritePrototype = {
img: null,
pos_x: 0,
pos_y: 0,
draw: function(context2d) {
/*do stuff with the canvas
using "this" to refer to
the object this method is
being called on*/
},
//Some more member functions...
}
From the concept of "objects inheriting from objects" that's usually advocated with prototype-friendly Javascript, I thought I could just do:
var player = Object.create(SpritePrototype);
But it turns out this approach is flawed, because the non-function fields will be the ones from SpritePrototype, as player's prototype is exactly SpritePrototype. That means I can't create more objects from that prototype or the non-function fields will get all mixed up.
So what's the point in Object.create, and more important, what would be the correct way to achieve what I'm trying to do? That is, how can I make "player" get a copy of the fields and inherit the functions from its prototype?
Again, I'm interested in doing things the way they're intended to be. I can always simulate inheritance by hand or skip it altogether. The point of my question is to understand prototypes and how and when they are useful, especially in my specific case.
Property values on prototypes are shared initially, but stop being shared when that property on an instance is written (assigned) to. At that moment, the instance gets its own version of the property. So it is not entirely correct to say the value is "shared". It is shared only up to the point in time at which the property on the instance is assigned to.
var SpritePrototype = {
img: 'img1'
};
var sprite1 = Object.create(SpritePrototype);
var sprite2 = Object.create(SpritePrototype);
sprite1.img = 'img2'; // does NOT affect prototype or sprite2
console.log(sprite2.img);
< "img1"
When img is referenced, its value is taken from the prototype. However, when img is written to, a new property is created on the instance to hold the new value, and used from then on.
The only way to change the property value on the prototype is to do so explicitly:
SpritePrototype.img = 'img3';
This will change img for all instances which have not yet defined their own local version of img by assigning to it.
You problem is, that you had defined "static" members in terms of object oriented design. Everything in protoype section is shared between objects. Function doesn't get actually copied to newly created object, but it is called with proper "this".
You should initialize your variables in constructor, like this
function Sprite(...) {
this.img = null;
...
}
Then for subtype you should use Object.create to create a prototype to inherit its methods, so
Player.prototype = Object.create(Sprite.prototype);
And finally, you may call parent constructor to initialize the variables
function Player(...) {
Sprite.call(this, ...);
}
PS. Constructor should be before Player.prototype assigning.
PPS. For more info see this https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create

Javascript: when to define functions inside constructor and when to use prototype?

I'm learning Javascript and have several questions concerning Javascript and OOP. I've noticed different declarations of functions in "classes" in various tutorials. First is the inside constructor:
Class = function () {
this.doSomething = function() {....};
}
And the other one is:
Class = function () {}
Class.prototype.doSomething = function() {....};
In which situations should the first construction be used, and in which situation should the second construction be used?
And the other question is: have I understood correctly that there's no protected properties or methods in js? What is to be used instead?
Thank you in advance!
When you define a function inside the constructor as this.myFunction=..., it is specific to your instance. This means that it must be constructed and kept in memory for all instances, which may be heavy. It also can't be inherited .
The only valid reason to do this are :
the enclosing of specific values
other types of specific functions (you might build a different function each time)
Most often, what you really need is a function defined on the prototype.
From the MDN on objects :
All objects in JavaScript are descended from Object; all objects
inherit methods and properties from Object.prototype, although they
may be overridden. For example, other constructors' prototypes
override the constructor property and provide their own toString
methods. Changes to the Object prototype object are propagated to all
objects unless the properties and methods subject to those changes are
overridden further along the prototype chain.
Regarding your additional question : the following code builds a non directly accessible function :
Class = function () {
var imprivate = function(){...};
this.doSomething = function() { uses imprivate};
}
A downside is that you have a different function instance for each instance of Class. This is most often done for modules (from which you have only one instance). Personally, I prefer to do exactly as suggested by ThiefMaster in comment : I prefix my private functions with _ :
// private method
XBasedGrapher.prototype._ensureInit = function() {

Categories