JS: Using dataset vs using (adhoc) properties - javascript

I need to store data (a string) on a certain HTML node.
This can be done via the dataset object
document.body.dataset.foo = 'bar';
Given the nature of JS however, node instances are also (just) objects, and thus you can also simply define a new property implicitly on the fly like so:
document.body.bar = 'foo';
Both methods fulfill the requirement of storing and then retrieving data of simple types.
I'm aware of the following differences:
dataset writes the the information as attribute on the node (data-{key}="{value}")
by using the subobject (dataset) you are less likely to overwrite an existing property
In my case, I would prefer not to have the information written as data-attribute, so I lean towards the property approach. Most sources on the internet however want you to use the dataset method.
Am I missing something here? Are there any other particularities to consider?

Related

Does it make diffrence in speed efficiency of code if we assign property of object to another variable and then use it?

I wonder does it make difference if I use another variable to access the property of object once and for all or access the data that I want , from the object each time :
data = {position: [{X:12},{Y:4}] ,name : 'Smth'}
is there any diffrent between the following method :
const X = data.position[0].X
for(...){
...do somthing with X
}
or
for(...){
...do somthing with data.position[0].X
}
Yes, some different is exist. Every time when you call property js interpreter try to find calling property (or method) at prototype hierarchy.
Most JavaScript engines use a dictionary-like data structure as storage for object properties - each property access requires a dynamic lookup to resolve the property's location in memory. This approach makes accessing properties in JavaScript typically much slower than accessing instance variables in programming languages like Java and Smalltalk.
If you use variable to cache some data it will be faster than you use direct access.
There is some great benchmark to test it.

Best (most performant) way to declare (class) properties with unknown values in v8

So I learned a bit about the hidden class concept in v8. It is said that you should declare all properties in the constructor (if using prototype based "pseudo classes") and that you should not delete them or add new ones outside of the constructor. So far, so good.
1) But what about properties where you know the type (that you also shouldn't change) but not the (initial) value?
For example, is it sufficient to do something like this:
var Foo = function () {
this.myString;
this.myNumber;
}
... and assign concrete values later on, or would it be better to assign a "bogus" value upfront, like this:
var Foo = function () {
this.myString = "";
this.myNumber = 0;
}
2) Another thing is with objects. Sometimes I just know that an object wont have a fixed structure, but I want to use it as a hash map. Is there any (non verbose) way to tell the compiler I want to use it this way, so that it isn't optimized (and deopted later on)?
Update
Thanks for your input! So after reading your comments (and more on the internet) I consider these points as "best practices":
Do define all properties of a class in the constructor (also applies for defining simple objects)
You have to assign something to these properties, even if thats just null or undefined - just stating this.myString; is apparently not enough
Because you have to assign something anyways I think assigning a "bogus" value in case you can't assign the final value immediatly cannot hurt, so that the compiler does "know" ASAP what type you want to use. So, for example this.myString = "";
In case of objects, do assign the whole structure if you know it beforehand, and again assign dummy values to it's properties if you don't know them immediatly. Otherwise, for example when intending to use the Object as a hashmap, just do: this.myObject = {};. Think its not worth indicating to the compiler that this should be a hashmap. If you really want to do this, I found a trick that assigns a dummy property to this object and deletes it immediatly afterwards. But I won't do this.
As for smaller Arrays it's apparently recommended (reference: https://www.youtube.com/watch?v=UJPdhx5zTaw&feature=youtu.be&t=25m40s) to preallocate them especially if you know the final size, so for example: this.myArray = new Array(4);
Don't delete properties later on! Just null them if needed
Don't change types after assigning! This will add another hidden class and hurt performance. I think thats best practice anyways. The only case where I have different types is for certain function arguments anyways. In that case I usually convert them to the same target type.
Same applies if you keep adding additional properties later on.
That being said, I also think doing this will lean to cleaner and more organized code, and also helps with documenting.
Yeah, so one little thing I am unsure remains: What if I define properties in a function (for example a kind of configure() method) called within the constructor?
Re 1): Just reading properties, like in your first snippet, does not do anything to the object. You need to assign them to create the properties.
But for object properties it doesn't actually matter much what values you initialise them with, as long as you do initialise them. Even undefined should be fine.
The concrete values are much more relevant for arrays, where you want to make sure to create them with the right elements (and without any holes!) because the VM tries to keep them homogeneous. In particular, never use the Array constructor, because that creates just holes.
Re 2): There are ways to trick the VM into using a dictionary representation, but they depend on VM and version and aren't really reliable. In general, it is best to avoid using objects as maps altogether. Since ES6, there is a proper Map class.

How to communicate different levels in the same javascript object

I am trying to namespace my jQuery app.
Example:
var app = {
data : {
data1: ''
},
fn: {
setData1: function(value){
app.data.data1 = value
}
}
}
This works, but is there a better solution which doesn't use the full path? Moreover, is this a good code structure for a medium-sized application?
Javascript does not have syntax for specifying shortcuts to a parent element in a nested literal object definition. And, in fact, Javascript a nested object inside another object could have been defined anywhere and the reference assigned into the object and could be assigned into many objects so there is no real such thing as a parent at runtime as far as Javascript is concerned. Javascript just doesn't work that way.
As such, if you want to reference another element of the object that is not below it, then you will have to use the full path of object names starting from the top of the object.
As to whether this is a good way to do this, that is entirely context-specific. At first glance (and with no other context supplied), I don't see why you need the fn or data levels in your object. Data properties and methods can be at the same level and that is commonly done without adding extra levels. You will find that extra levels that aren't necessary just increase the amount of typing in your code and do not improve performance. You should give your methods and data properties meaningful enough names that it's very clear what they are and what they are used for. A name like data1 is not a particularly good example.
In addition, there is generally not a reason to make a setData1() method for a public data1 property, but if you put them at the same level, you can do use the this pointer in the method which will be set to app if someone calls app.setData1():
var app = {
data1: ''
setData1: function(value){
this.data1 = value;
}
}

Associate Data With HTML Element (without jQuery)

I need to associate some data with an HTML Element, hopefully avoiding any memory leaks. Obviously a simple solution would be to throw some kind of identifier on the element, and then create a dictionary mapping that identifier to the data I need. However, this is in a javascript library that users will add to their page, thus I don't have control over when elements are added or removed.
What I'm looking to do is associate data with an HTML element while it's on the page, while allowing for that data to be GC'd when/if the element is removed. Is there a way to do this that doesn't involve writing my own periodic GC to clean up orphaned data? Is it safe to add properties to HTML elements?
Attribute approach
You can store data in elements using custom data-* attributes.
This has the limitation that you can only store strings, but you can use JSON to store plain objects or arrays (not by reference).
To avoid conflicts with other code, it would be a good idea to include the name of your library in the attribute.
They can be set directly in the HTML:
<div data-mylibrary-foo="bar"></div>
And they can be read or written with JavaScript:
element.getAttribute('data-mylibrary-foo'); // read (old way)
element.setAttribute('data-mylibrary-foo', 'bar'); // write (old way)
element.dataset.mylibraryFoo; // read (new way)
element.dataset.mylibraryFoo = 'bar'; // write (new way)
They can also be read by CSS, using some attribute selector.
Property approach
This is much more flexible than the attribute approach, allowing to store arbitrary data in your element.
To avoid conflicts with other code, better wrap all properties in an object with the name of your library:
element.mylibrary = {};
element.mylibrary.foo = 'bar';
The problem is that a future HTML standard could define mylibrary as a native property, so there could be problems.
Symbol approach
ECMAScript 6 introduces symbols, which can be used as properties. The advantage is that each symbol has an unique identity, so you don't need to worry about some other library or a future standard using the same properties as your code.
var foo = Symbol("foo");
element[foo] = 'bar';
WeakMap approach
ECMAScript 6 introduces WeakMaps, which allow you to associate data with objects in a way that, if the objects are no longer referenced anywhere else, they will be garbage collected.
Like the property approach, they allow you to store arbitrary data.
Since the data is not stored in the elements themselves, there is no risk of conflicts.
var allData = new WeakMap();
var data1 = allData.get(element1) || {}; // read
data1.foo = "bar";
allData.set(element1, data1); // write
An HTML element in JavaScript is just a JavaScript object, so you can certainly add arbitrary properties to it. JavaScript will handle the garbage collection just fine.
The data- attribute is another option and might be a better choice if your properties are strings; their values will be visible in the DOM (which might be a good thing for debugging). If your properties are themselves objects, then you'd have to stringify them (and reverse the process to retrieve their values).

Making an associative array in Javascript that has custom get/put operations

I need to make a Javascript object that would behave as an associative array, but with some functions that are called before getting and setting properties.
For example, the task may be like this: we should make an object, that would contain a squared value of a key, like this:
obj.two should be equal to 4,
obj.four should be equal to 16,
obj['twenty one'] should be equal to 441.
This is an example. Actually I need to make setting operation overridden too. The getting and setting operations would go to the database, and they not necceserily would take strings as keys, but any types of objects, from which it would create a DB query.
How would I do that a) with as less thirdparty libraries as possible and b) to make it work on as much platforms as possible?
I am new to JS, I've found that JS has no associative arrays, relying on the ability to define objects on the fly with arbitrary properties. I googled and had an idea to use or even override lookupgetter (and setter), where define a new getter/setter on the fly, but I coundn't find if the interpreter would use this method every time it encounters new key. Anyway, it looks like I wouldn't be able to use anything except strings or maybe numbers as keys.
In Java, I would just implement java.util.Map.
Please help me, how would I do the same in Javascript?
edit
I think I will get what I want if I manage to override [[Get]] and [[Put]] methods mentioned here http://interglacial.com/javascript_spec/a-8.html#a-8.6.2.1
For your example, doesn't this do what you want:
var myObj = {};
myObj["two"] = 4;
myObj["four"] = 16;
myObj["twenty one"] = 441;
alert(myObj["four"]); // says 16
Or are you trying to say that the object should magically calculate the squares for you?
JavaScript object keys are strings. If you try to use a number as a key JavaScript basically converts it to a string first.
Having said that, you can use objects as keys if you define a meaningful toString method on them. But of course meaningful is something that happens on a case by case basis and only you will know what needs to be done for your case.
You can also define objects that maintain their own internal data structures which you access via object methods. I think explaining that is beyond the scope of this post. Google "javascript module pattern" for some pointers to get you started.
See http://ejohn.org/blog/javascript-getters-and-setters/
Also this particular answer: Javascript getters and setters for dummies?
edit
According to Does JavaScript have the equivalent of Python's __getattribute__? and Is there an equivalent of the __noSuchMethod__ feature for properties, or a way to implement it in JS? there is no nice way of accomplishing exactly what the OP wants. Getters and setters are not useful because you must know the name of what you're looking for in advance.
My recommendation would thus be to do something like:
var database = {}
database.cache = {}
database.get = function(key) {
// INSERT CUSTOM LOGIC to recognize "forty-two"
if (!(key in database.data))
database.cache[key] = fetch_data_from_database();
return database.cache[key];
}
database.put = function(key, value) {
database.cache[key] = value;
send_data_to_database(key, value);
}
I decided that the most correct way to implement this is to use Harmony:Proxies. It isn't working on all platforms, but it lets implement this in the most seamless way; and it may be supported in more platforms in the future.
This page contains an example that I used as a template to do what I want:
http://wiki.ecmascript.org/doku.php?id=harmony:proxies

Categories

Resources