When using a WeakMap Object in JavaScript, the get and set methods require a Key to be passed in as argument.
FYI, I'm using WeakMap to simulate private properties for a class, something like this:
window.MyObject = (function(){
let _privateProperty = new WeakMap();
class MyObject {
constructor(value) {
_privateVariable.set(this, value);
}
SamplePrivateGet() {
return _privateVariable.get(this);
}
}
return MyObject;
})();
My question is: is get and set performance influenced by the object used as key?
I know primitive types can't be used as key, but maybe using an object with only one property as key could be faster than using an object that has, say, one thousand properties.
I tried looking at ECMA specifications but it's not specified, my guess is it's because it would depends on the browser's implementation.
EDIT: There are two ways to approach this with WeakMaps, one is declaring a single _privateProperties WeakMap to which I will add my private properties, the other declare one WeakMap for each of the private properties. I'm currently using the second approach so that every WeakMap can be Garbage Collected separately, but maybe going with the first would allow me to make much less .get calls. But I guess that's out of the scope of the question :)
Not sure if this is good idea, but you could make kind of private instance properties by using Symbols.
const PRIVATE_VAR = Symbol('MY_CLASS::PRIVATE_VAR');
const PRIVATE_FUNC = Symbol('MY_CLASS::PRIVATE_FUNC');
export default class MyClass{
constructor(value){
this[PRIVATE_VAR] = value;
}
[PRIVATE_FUNC](){
/* ... */
}
}
Without access to those Symbols values, it is quite hard to access those specific instance properties (plus instance properties defined with Symbols are non-enumerable in classes and objects).
btw 'Private instance methods and accessors' are at stage 3 so such solutions might not be relevant soon
https://github.com/tc39/proposals
Related
Let's say I want to create an array of Person using a random data library.
I could do something like
import {generateRandom} from 'someLib'
let people = []
function getPerson() ({
name: generateRandom.string()
age: generateRandom.number()
})
for (let i = 0; i < 10; i++) {
people.push(getPerson())
}
But I could also do something like
import {generateRandom} from 'someLib'
class Person {
constructor() {
this.name = generateRandom.string(),
this.age = generateRandom.number()
}
}
let people = []
for (let i = 0; i < 10; i++) {
people.push(new Person())
}
On a memory level, is there any difference in the outcome?
(This is just a theoretical question, I am not trying to solve any problem in particular)
I have found this question that is related to this Difference between creating a class in javascript to create an object and creating an class and object in Java
Which states that there are no classes in JS.
Is this just syntactic sugar? 2 ways of doing exactly the same thing?
If you want to examine the memory yourself, you can put this code in the console:
class Test{};
const test = new Test();
class Class {};
test.Class = Class;
test.classObj = new Class();
function func() {return {};};
test.func = func;
test.funcObj = func();
Take a heap snapshot with google chrome dev tools, sort by constructor and find the Test class.
You can then examine the memory of these functions and objects. Here's a screenshot of what I got:
You can see that the object instantiated by the class constructor is slightly larger than the one instantiated by the function. Expanding the prototype, you can see they both use the Object constructor, but the classObj has the additional Class constructor in its prototype chain.
You can also see that the class constructor appears to retain more memory than regular functions, retained size meaning memory that will be cleaned up by garbage collection if the function is no longer in use.
Seems to be an extra 172 bytes for the class constructor, and 24 bytes per object for an empty object.
Following VLAZ's comment, here's the results with 10 methods added, and 10 instances.
class Test{};
const test = new Test();
class Class {
method0(){};
method1(){};
method2(){};
method3(){};
method4(){};
method5(){};
method6(){};
method7(){};
method8(){};
method9(){};
};
test.Class = Class;
for (let i=0; i < 10; i++){
test["classObj" + i] = new Class();
}
function func0(){};
function func1(){};
function func2(){};
function func3(){};
function func4(){};
function func5(){};
function func6(){};
function func7(){};
function func8(){};
function func9(){};
function constructorFunc() {
return {
method0: func0,
method1: func1,
method2: func2,
method3: func3,
method4: func4,
method5: func5,
method6: func6,
method7: func7,
method8: func8,
method9: func9,
};
};
test.constructorFunc = constructorFunc;
for (let i=0; i < 10; i++){
test["funcObj" + i] = constructorFunc();
}
The shallow size of the class objects are now much smaller. This seems to be due to the fact that they can just store a reference to the class prototype rather than reference all of their methods directly.
At first glance, the retained size of Class seems to be smaller than constructorFunc, but expanding Class you can see a property named prototype which is an object retaining an extra 1.38 KB. Adding that to the 520 B of the class itself pushes it above the retained memory of constructorFunc. But the memory saved by creating instances of the class instead of an object will outweigh that pretty quick.
So seems like classes are the way to go.
Your first getPerson() is invalid syntax and the function does not even attempt to return anything (if it was valid syntax). So, people.push(getPerson()) would generate an array of undefined(if the syntaxError was fixed) which will be entirely different than your second code block which generates an array of objects.
If, what you meant to ask about was something like this:
let people = []
function getPerson() {
return {
name: generateRandom.string()
age: generateRandom.number()
}
}
for (let i = 0; i < 10; i++) {
people.push(getPerson())
}
Then, this and your class will each create an array of objects and those objects will each have your two properties on them. The Class will contain some additional setup on the prototype that allows for subclassing, but if you're just asking about using the object with two properties, then these two would fundamentally do the same thing.
On a memory level, is there any difference in the outcome?
Without methods, your two properties will occupy the same amount of memory. The two properties are assigned directly to the object so those two properties use the same memory.
The class will actually set up a few other things on the object like a .constructor property and the object will get its own separate prototype. The prototype is shared among all instances. So, there could be slightly more memory usage for the class instance, but it's unlikely this makes a material difference.
If you defined methods directly on the object in the first implementation and methods in the class in the second implementation, then the class would definitely be more efficient because there would be one copy of the methods on the prototype vs. many separate copies of the methods on each instance of the object. But, you didn't show that in your question.
Which states that there are no classes in JS. Is this just syntactic sugar? 2 ways of doing exactly the same thing?
Classes are syntactic sugar, but they make it easy to define things in an efficient way without having to do a bunch of manual things. And, because the language sets things up for you, then everyone using a class definition creates code that works the same way.
You can manually build the same object that instantiating an instance of a class does, but it's a lot more code to do write all the bookkeeping that a class does for you. Your simple object with just two instance properties doesn't show any of that or need any of that, but when you start having methods and sub-classing things and overriding base methods and then calling base methods in your derived implementation, the class definition takes care of a some bookkeeping for you and simply lets you write, good extensible code faster and with everyone doing it the same way. Again, your simple object with just two properties does not show or need what a class does, but other uses of objects do.
For example, the class definition and new Person() creates an object that automatically sets yourObject.constructor to be the constructor that created the object. This allows other code to know what type of object it is or allows other code to abstractly create new instances of the same object. Methods are put on the prototype of the object and made non-enumerable. Constructors can call super(...) to execute the base class constructor (whatever it happens to be). Without the class definition, you have to do that very manually by calling the exact right function in the base class.
When I instantiate an object in my javascript framework, I add a __listeners__ property to it. But then the console.log output shows the__listeners__ property first.
I don't want the __listeners__ property showing up in the beginning of the console.log output, since its an internal property in my framework. I'm looking for a way to somehow push the __listeners__ property to the end of the object.
The only solution I have so far is by adding an extra object in the prototype chain.
create: function() {
var objTemplate=Object.create(this);
var newObj=Object.create(objTemplate);
objTemplate.__listeners__=[];
return newObj;
}
But, the above solution, will cause slower lookups through the prototype chain and creates a slightly unintuitive object hierarchy.
Is this ok or is there a better solution to this problem?
If you can use ES2015's WeakMap, which cannot be shimmed on older environments (but see ¹), creating information related to an object that isn't stored on the object is one of their use cases:
const privateDataCache = new WeakMap();
class MyThingy {
constructor() {
privateDataCache.set(this, {listeners: []});
// ...
}
addListener(listener) {
privateDataCache.get(this).listeners.push(listener);
}
removeListener(listener) {
const data = privateDataCache.get(this);
data.listeners = data.listeners.filter(l => l !== listener);
}
}
Because the keys in a WeakMap are weakly-held, they don't prevent your objects from being garbage collected, and the private data stored in the WeakMap is removed when the key becomes invalid.
That won't work in IE prior to IE11, though. (And IE11's WeakMap is missing some things, but nothing the above relies on.)
¹ Although WeakMap cannot be correctly shimmed/polyfilled, the syntax can be shimmed/polyfilled. core.js does that, implementing the WeakMap by storing data on the key itself. So while not correct, it may well be what you want for your use case.
So the code below reflects the pseudo-classical version of inheritance in JavaScript.
function SynthOne() { // constructor 1
this.sound1 = "piano";
};
function SynthTwo() { // constructor 2
this.sound2 = "bass";
}
SynthOne.prototype = new SynthTwo; // overwrite constructor 1's prototype with constructor 2's
var synthsCombined = new SynthOne; // assign constructor 1 to variable
// this variable now has access to both constructors properties & methods
document.write(synthsCombined.sound1 + " ")
document.write(synthsCombined.sound2)
But let's change this to make sound1 and sound2 to simply sound.
Let's also assume that I really wanted to create an inheritance chain to access both of these "sounds" even if they were named the same thing. Is there a pattern in the JavaScript community or a coding convention that exist to deal with this kind of situation? Or am I just stuck?
Thank you
Child properties hide properties of the same name further up the prototype chain. Technically, you can still get access to the parent property like this:
Object.getPrototypeOf(synthsCombined).sound
Or the non-standard:
synthsCombined.__proto__.sound
But this probably isn't something you want to do. If you need both values, then name them different things.
it was simply something that entered my mind and I was curious about. I can see a situation where at the very least you combine constructors not realizing they have similar property/method names.
You hardly inherit from classes whose set of properties1 you do not know. If you subclass something, you often want to explicitly overwrite properties with more specific values - that's just what the shadowing is for.
In case you want to extend the set, you'd have to choose an unallocated name. In case of interface clashes (e.g. when extending the implementation), that's just a bug and either the base class or the child classes would need to change their identifier. Using descriptive names will lower the risk.
How to deal with this kind of situation?
If it's unwanted, fix the bug (this is not JavaScript-specific). If you deliberately have chosen the same property name, you can access the inherited value by manually ascending the prototype chain with Object.getPrototypeOf().
[1]: Speaking of both attributes and methods, as they're just properties in javascript
You could give one of your constructors a base property, which will get the properties from the inherited constructor:
function SynthOne() { // constructor 1
this.base = {};
this.sound = "piano";
SynthTwo.call(this.base);
};
function SynthTwo() { // constructor 2
this.sound = "bass";
}
SynthOne.prototype = Object.create(SynthTwo.prototype);
var synthsCombined = new SynthOne;
console.log(synthsCombined.sound); //piano
console.log(synthsCombined.base.sound); //bass
But from what it looks like you are trying to accomplish, maybe inheritance is not the right way for you. It might make more sense to create a generic Synth class and maybe a SynthCollection class, to combine different Synths.
I've read through most of the Javascript inheritance references, but I'm afraid I'm still scratching my head as to how to do this.
I'm trying to put together several classes, in the general sense, that will have similar behavior and thought using prototypes might be a way to achieve that. So I create a base class as follows:
function my_base_class()
{
var a_common_object = undefined; // The true value of this can't be set until runtime.
// some other stuff ...
};
Ideally, I'd like a_common_object to be private or at least protected, but just getting it working would be a good first step. I then need to create several derived classes of which this might be one:
function my_derived_class()
{
this.do_something_to_common_object = function()
{
// Here I need to reference my_base_class.a_common_object but
// at this point there's no relationship between the two classes
};
};
I now set the prototype of my_derived_class while creating an instance:
my_derived_class.prototype = new my_base_class();
var my_derived_class_inst = new my_derived_class();
So at this point I'm hoping that I have an object - my_derived_class_inst which has traits of my_base_class including the static object a_common_object which I can access.
I have two questions:
How do I refer to a_common_object within my_derived_class when
there's no relationship established between the two classes?
How can I then change a_common_object to its true value, so that
all derived classes seamlessly pick up the new value.
Please don't simply refer me to the standard reference web sites on inheritance as I've read most of them through and I'm still no wiser. It seems to me that the answer should be really simple but so far it escapes me. Many thanks.
do_something_to_common_object() really doesn't have a way of reaching a_common_object directly.
a_common_object isn't a member of the instance created for the prototype. It's a local variable scoped inside the constructor. So, only a function that's also defined within the constructor can reach it (ref: closures):
function my_base_class()
{
var a_common_object = undefined;
Object.defineProperty(this, 'a_common_object', {
get: function () {
return a_common_object;
}
});
// ...
}
function my_derived_class()
{
this.do_something_to_common_object = function()
{
console.log(this.a_common_object); // uses getter to retrieve the value
};
};
It would still be publicly accessible, but your options are limited as JavaScript doesn't yet support or have an equivalent to access modifiers.
Though, with Object.defineProperty(), it would at least be read-only so far and non-enumerable by default (won't appear in a for..in loop).
At least until #2, where you'd need to also have a setter. Though, it would be a chance to validate the value being storing it.
Object.defineProperty(this, 'a_common_object', {
// ....
set: function (value) {
if (/* validator */) {
a_common_object = value;
}
}
});
I'm working on a new OOP model for JavaScript and I'm wondering whether you consider it right to make methods on objects enumerable or only the data members. I can see some sense in both and maybe there is no definite answer.
Can also make the own methods enumerable and the inherited ones not...
That said I feel it makes sense anyways to make all data members enumerable even if they are inherited.
update: this seemed not clear from what people are answering. I am creating a OOP model which will allow users to write something like this to declare a class:
update 2: in the mean time the project is out and about, this is what it has become: OoJs. In it, user defined properties including methods are enumerable, properties added by the framework aren't.
;(function( namespace )
{
'use strict';
namespace.Shape = Shape
var Static = namespace.OoJs.setupClass( "Shape" )
// Data members
//
Static.canvas = null
Static.Protected( "canvas" ) // Protected members
Static.Public () // Public members
// constructor
//
function Shape()
{
// Data members
//
this.sides = null
// Private methods
//
this.init = init
this.Protected( "sides" ) // Protected members
var iFace = this.Public( getOffset ) // Public interface
this.init() // for example
return iFace
}
// Method definitions
//
function init (){ /*do something useful*/ }
function getOffset(){ return [ this.x, this.y ] }
})( window )
So the question is if you would use this to declare your classes, would you assume/want methods to be enumerable or not or should there be a way to configure either classwide or per member whether it should be enumerable not?
Unless there are some semantics associated with the keys -- you encourage users of your object to iterate over its properties -- then there's neither an advantage nor a disadvantage to having methods be enumerable.
You gain nothing by hiding them, and IDEs and other tools that interpret or partially evaluate your script can always use getOwnPropertyNames to provide for auto-completion and the like.
Object.getOwnPropertyNames
Returns an array of all properties (enumerable or not) found directly upon a given object.
After consideration, Felix Kling's comment leads to the answer.
As it depends on the situation and since I don't know my user's situation, I shouldn't limit their options and at least offer a featureset as rich as the native Object model. I will allow for users to set options on their properties similar to Object.defineProperty.
I think that keeping the user's options open should be considered an important programming principle, so this is not a hard decision.
The question remains over what the defaults should be. In standard javascript properties added to prototypes are all enumerable.