I was reading over the draft for ES6, and I noticed this note in the Object.prototype.toString section:
Historically, this function was occasionally used to access the string
value of the [[Class]] internal property that was used in previous
editions of this specification as a nominal type tag for various
built-in objects. This definition of toString preserves the ability to
use it as a reliable test for those specific kinds of built-in objects
but it does not provide a reliable type testing mechanism for other
kinds of built-in or program defined objects.
From reading this thread on es-discuss, it sounds like [[Class]] is being replaced with [[NativeBrand]] in the ES6 draft so that they can specify it as being non-extensible (those were at least Allen Wirfs-Brock's thoughts).
Curious, I ran a quick test in FireFox and Chrome (with experimental JavaScript enabled):
Object.prototype.toString.apply(new WeakMap());
=> '[object WeakMap]'
"WeakMap" is not one of the [[NativeBrand]]s specified in the ES6 draft. However, this test returned "[object WeakMap]" on both browsers.
So I'm confused. I have a few questions.
1. Do Chrome and Firefox behave correctly?
From one way of reading the draft it sounds like they should return [object Object] (and all of this is pretty new, so I wouldn't be surprised to see this change in future editions of these browsers). However, it's hard for me to understand the intention of this section of the draft, especially since there are some places with "???".
Does anyone who has been following es-discuss more fervently have any relevant information? Or anyone who can understand the draft language better?
2. Is there an alternative to Object.prototype.toString?
From the note quoted above it makes it sound as if Object.prototype.toString is retained for legacy reasons, as if there's something new now that should be used instead. Especially the part of the node that reads "it does not provide a reliable type testing mechanism for other kinds of built-in ... objects". Does that mean that future built-ins can't be tested with this method?
Let's use a concrete example.
If I want to ensure an object I have received from an unknown source is a String object (an actual constructed String object, not a primitive string), I could do:
if (Object.prototype.toString.apply(unknownObject) != '[object String]')
throw new TypeError('String object expected.');
This lets me know if unknownObject is a String object no matter what frame it was constructed in.
My question is, should this be the approach I take moving forward into ES6? Or is there an alternative? Something like Object.getNativeBrandOf?
3. Since [[NativeBrand]] seems like it won't include future types of objects, how would one test for these objects?
Will this work?
if (Object.prototype.toString.apply(unknownObject) != '[object Symbol]')
throw new TypeError('Symbol expected.');
...assuming Symbol is the eventual name for Private Names.
Should I use this?
if (Object.prototype.toString.apply(unknownObject) != '[object WeakMap]')
throw new TypeError('WeakMap expected.');
... or something else?
The reason I ask is I am currently writing code that I want to be able to transition as easily as possible to ES6 in a year or two when possible. If there is a replacement for Object.prototype.toString, then I can just shim it in and continue from there. Thanks!
Update
benvie's answer provided me with the correct term to search for and understand the answer to my questions.
I found an email from Allen Wirfs-Brock on es-discuss concerning this issue.
Here's what I found, for anyone else asking the same questions:
1. Do Chrome and Firefox behave correctly?
Yes, why is explained below.
2. Is there an alternative to Object.prototype.toString?
As it is now, there will be a couple "alternatives" in the sense of possibilities, but not in the sense of replacements.
a. Using the ##toStringTag symbol. However, my understanding is that Object.prototype.toString should still probably be used. ##toStringTag is provided to allow extending the results that can be returned from Object.prototype.toString. If you have a prototype you would like to add your own string tag to, you could use ##toStringTag to set the value to any string. Object.prototype.toString will return this value except in the case where this value is one of the ES5 built-ins, in which case the string tag will be prepended with '~'.
b. Using private symbols on user-defined objects. I read one email promoting this as the best way to do the same type of check on a user-defined object. However, I don't see how that really settles the issue, as I fail to understand how it could be a cross-frame solution and it doesn't let you check against ES6 built-ins.
So even though there are some alternatives, it's good to stick with Object.prototype.toString now and going forward, with one caveat:
It'll work to make sure you have an ES5 built-in, such as String, but it won't be fool-proof to make sure you have an ES6 built-in because they can be spoofed with ##toStringTag. I'm not sure why this is, and I may be missing something, or it could change as the spec evolves.
3. Since [[NativeBrand]] seems like it won't include future types of objects, how would one test for these objects?
As mentioned above, Object.prototype.toString can still be used on ES6 built-ins, but it's not fool-proof as it can be spoofed by anyone with access to the ##toStringTag symbol. However, maybe there shouldn't be a fool-proof method, since Object.prototype.toString(weakmap) == '[object WeakMap]' doesn't mean that weakmap instanceof WeakMap (and it shouldn't!). The weakmap could have come from another frame, or it could be a user-created weakmap-like object. The only thing you really know is it reports to be functionally equivalent to a WeakMap.
It does seem to beg the question why you can't have a user-defined object which reports to be functionally equivalent to a String or Array (sans the prefixed "~").
This is currently a moving target in the ES6 spec. For the existing set of objects, the existing mechanics are maintained for various reasons including compatibility. In the most recent ES6 spec, published October 26th, you can find some hints of the potential future direction
15.4.6.2.4 ArrayIterator.prototype.##toStringTag
The initial value of the ##toStringTag property is the
string value "Array Iterator".
15.14.5.13 Map.prototype.##toStringTag
The initial value of the ##toStringTag property is the string value "Map".
You can find the original discussion that originated this in this thread on es-discuss
Related
Objects in JavaScript can be used as Hashtable
(the key must be String)
Is it perform well as Hashtable the data structure?
I mean , does it implemented as Hashtable behind the scene?
Update: (1) I changed HashMap to hashtable (2) I guess most of the browser implement it the same, if not why not? is there any requirement how to implement it in the ECMAScript specs?
Update 2 : I understand, I just wonder how V8 and the Firefox JS VM implements the Object.properties getters/setters?
V8 doesn't implement Object properties access as hashtable, it actually implement it in a better way (performance wise)
So how does it work? "V8 does not use dynamic lookup to access properties. Instead, V8 dynamically creates hidden classes behind the scenes" - that make the access to properties almost as fast as accessing properties of C++ objects.
Why? because in fixed class each property can be found on a specific fixed offset location..
So in general accessing property of an object in V8 is faster than Hashtable..
I'm not sure how it works on other VMs
More info can be found here: https://v8.dev/blog/fast-properties
You can also read more regarding Hashtable in JS here:(my blog) http://simplenotions.wordpress.com/2011/07/05/javascript-hashtable/
"I guess most of the browser implement it the same, if not why not? is there any requirement how to implement it in the ECMAScript specs?"
I am no expert, but I can't think of any reason why a language spec would detail exactly how its features must be implemented internally. Such a constraint would have absolutely no purpose, since it does not impact the functioning of the language in any way other than performance.
In fact, this is absolutely correct, and is in fact the implementation-independence of the ECMA-262 spec is specifically described in section 8.6.2 of the spec:
"The descriptions in these tables indicate their behaviour for native
ECMAScript objects, unless stated otherwise in this document for particular kinds of native ECMAScript objects. Host objects may support these internal properties with any implementation-dependent behaviour as long as it is consistent with the specific host object restrictions stated in this document"
"Host objects may implement these internal methods in any manner unless specified otherwise;"
The word "hash" appears nowhere in the entire ECMA-262 specification.
(original, continued)
The implementations of JavaScript in, say, Internet Explorer 6.0 and Google Chrome's V8 have almost nothing in common, but (more or less) both conform to the same spec.
If you want to know how a specific JavaScript interpreter does something, you should research that engine specifically.
Hashtables are an efficient way to create cross references. They are not the only way. Some engines may optimize the storage for small sets (for which the overhead of a hashtable may be less efficient) for example.
At the end of the day, all you need to know is, they work. There may be faster ways to create lookup tables of large sets, using ajax, or even in memory. For example see the interesting discussion on this post from John Reseig's blog about using a trie data structure.
But that's neither here nor there. Your choice of whether to use this, or native JS objects, should not be driven by information about how JS implements objects. It should be driven only by performance comparison: how does each method scale. This is information you will get by doing performance tests, not by just knowing something about the JS engine implementation.
Most modern JS engines use pretty similar technique to speed up the object property access. The technique is based on so called hidden classes, or shapes. It's important to understand how this optimization works to write efficient JS code.
JS object looks like a dictionary, so why not use one to store the properties? Hash table has O(1) access complexity, it looks like a good solution. Actually, first JS engines have implemented objects this way. But in static typed languages, like C++ or Java a class instance property access is lightning fast. In such languages a class instance is just a segment of memory, end every property has its own constant offset, so to get the property value we just need to take the instance pointer and add the offset to it. In other words, in compile time an expression like this point.x is just replaced by its address in memory.
May be we can implement some similar technique in JS? But how? Let's look at a simple JS function:
function getX(point) {
return point.x;
}
How to get the point.x value? The first problem here is that we don't have a class (or shape) which describes the point. But we can calculate one, that is what modern JS engines do. Most of JS objects at runtime have a shape which is bound to the object. The shape describes properties of the object and where these properties values are stored. It's very similar to how a class definition describes the class in C++ or Java. It's a pretty big question, how the Shape of an object is calculated, I won't describe it here. I recommend this article which contains a great explanation of the shapes in general, and this post which explains how the things are implemented in V8. The most important thing you should know about the shapes is that all objects with the same properties which are added in the same order will have the same shape. There are few exceptions, for example if an object has a lot of properties which are frequently changed, or if you delete some of the object properties using delete operator, the object will be switched into dictionary mode and won't have a shape.
Now, let's imagine that the point object has an array of property values, and we have a shape attached to it, which describes where the x value in this property array is stored. But there is another problem - we can pass any object to the function, it's not even necessary that the object has the x property. This problem is solved by the technique called Inline caching. It's pretty simple, when getX() is executed the first time, it remembers the shape of the point and the result of the x lookup. When the function is called second time, it compares the shape of the point with the previous one. If the shape matches no lookup is required, we can take the previous lookup result.
The primary takeaway is that all objects which describe the same thing should have the same shape, i.e. they should have the same set of properties which are added in the same order. It also explains why it's better to always initialize object properties, even if they are undefined by default, here is a great explanation of the problem.
Relative resources:
JavaScript engine fundamentals: Shapes and Inline Caches and a YouTube video
A tour of V8: object representation
Fast properties in V8
JavaScript Engines Hidden Classes (and Why You Should Keep Them in Mind)
Should I put default values of attributes on the prototype to save space?
this article explains how they are implemented in V8, the engine used by Node.js and most versions of Google Chrome
https://v8.dev/blog/fast-properties
apparently the "tactic" can change over time, depending on the number of properties, going from an array of named values to a dictionary.
v8 also takes the type into account, a number or string will not be treated in the same way as an object (or function, a type of object)
if i understand this correctly a property access frequently, for example in a loop, will be cached.
v8 optimises code on the fly by observing what its actually doing, and how often
v8 will identify the objects with the same set of named properties, added in the same order (like a class constructor would do, or a repetitive bit of JSON, and handle them in the same way.
see the article for more details, then apply at google for a job :)
Objects in JavaScript can be used as Hashtable
(the key must be String)
Is it perform well as Hashtable the data structure?
I mean , does it implemented as Hashtable behind the scene?
Update: (1) I changed HashMap to hashtable (2) I guess most of the browser implement it the same, if not why not? is there any requirement how to implement it in the ECMAScript specs?
Update 2 : I understand, I just wonder how V8 and the Firefox JS VM implements the Object.properties getters/setters?
V8 doesn't implement Object properties access as hashtable, it actually implement it in a better way (performance wise)
So how does it work? "V8 does not use dynamic lookup to access properties. Instead, V8 dynamically creates hidden classes behind the scenes" - that make the access to properties almost as fast as accessing properties of C++ objects.
Why? because in fixed class each property can be found on a specific fixed offset location..
So in general accessing property of an object in V8 is faster than Hashtable..
I'm not sure how it works on other VMs
More info can be found here: https://v8.dev/blog/fast-properties
You can also read more regarding Hashtable in JS here:(my blog) http://simplenotions.wordpress.com/2011/07/05/javascript-hashtable/
"I guess most of the browser implement it the same, if not why not? is there any requirement how to implement it in the ECMAScript specs?"
I am no expert, but I can't think of any reason why a language spec would detail exactly how its features must be implemented internally. Such a constraint would have absolutely no purpose, since it does not impact the functioning of the language in any way other than performance.
In fact, this is absolutely correct, and is in fact the implementation-independence of the ECMA-262 spec is specifically described in section 8.6.2 of the spec:
"The descriptions in these tables indicate their behaviour for native
ECMAScript objects, unless stated otherwise in this document for particular kinds of native ECMAScript objects. Host objects may support these internal properties with any implementation-dependent behaviour as long as it is consistent with the specific host object restrictions stated in this document"
"Host objects may implement these internal methods in any manner unless specified otherwise;"
The word "hash" appears nowhere in the entire ECMA-262 specification.
(original, continued)
The implementations of JavaScript in, say, Internet Explorer 6.0 and Google Chrome's V8 have almost nothing in common, but (more or less) both conform to the same spec.
If you want to know how a specific JavaScript interpreter does something, you should research that engine specifically.
Hashtables are an efficient way to create cross references. They are not the only way. Some engines may optimize the storage for small sets (for which the overhead of a hashtable may be less efficient) for example.
At the end of the day, all you need to know is, they work. There may be faster ways to create lookup tables of large sets, using ajax, or even in memory. For example see the interesting discussion on this post from John Reseig's blog about using a trie data structure.
But that's neither here nor there. Your choice of whether to use this, or native JS objects, should not be driven by information about how JS implements objects. It should be driven only by performance comparison: how does each method scale. This is information you will get by doing performance tests, not by just knowing something about the JS engine implementation.
Most modern JS engines use pretty similar technique to speed up the object property access. The technique is based on so called hidden classes, or shapes. It's important to understand how this optimization works to write efficient JS code.
JS object looks like a dictionary, so why not use one to store the properties? Hash table has O(1) access complexity, it looks like a good solution. Actually, first JS engines have implemented objects this way. But in static typed languages, like C++ or Java a class instance property access is lightning fast. In such languages a class instance is just a segment of memory, end every property has its own constant offset, so to get the property value we just need to take the instance pointer and add the offset to it. In other words, in compile time an expression like this point.x is just replaced by its address in memory.
May be we can implement some similar technique in JS? But how? Let's look at a simple JS function:
function getX(point) {
return point.x;
}
How to get the point.x value? The first problem here is that we don't have a class (or shape) which describes the point. But we can calculate one, that is what modern JS engines do. Most of JS objects at runtime have a shape which is bound to the object. The shape describes properties of the object and where these properties values are stored. It's very similar to how a class definition describes the class in C++ or Java. It's a pretty big question, how the Shape of an object is calculated, I won't describe it here. I recommend this article which contains a great explanation of the shapes in general, and this post which explains how the things are implemented in V8. The most important thing you should know about the shapes is that all objects with the same properties which are added in the same order will have the same shape. There are few exceptions, for example if an object has a lot of properties which are frequently changed, or if you delete some of the object properties using delete operator, the object will be switched into dictionary mode and won't have a shape.
Now, let's imagine that the point object has an array of property values, and we have a shape attached to it, which describes where the x value in this property array is stored. But there is another problem - we can pass any object to the function, it's not even necessary that the object has the x property. This problem is solved by the technique called Inline caching. It's pretty simple, when getX() is executed the first time, it remembers the shape of the point and the result of the x lookup. When the function is called second time, it compares the shape of the point with the previous one. If the shape matches no lookup is required, we can take the previous lookup result.
The primary takeaway is that all objects which describe the same thing should have the same shape, i.e. they should have the same set of properties which are added in the same order. It also explains why it's better to always initialize object properties, even if they are undefined by default, here is a great explanation of the problem.
Relative resources:
JavaScript engine fundamentals: Shapes and Inline Caches and a YouTube video
A tour of V8: object representation
Fast properties in V8
JavaScript Engines Hidden Classes (and Why You Should Keep Them in Mind)
Should I put default values of attributes on the prototype to save space?
this article explains how they are implemented in V8, the engine used by Node.js and most versions of Google Chrome
https://v8.dev/blog/fast-properties
apparently the "tactic" can change over time, depending on the number of properties, going from an array of named values to a dictionary.
v8 also takes the type into account, a number or string will not be treated in the same way as an object (or function, a type of object)
if i understand this correctly a property access frequently, for example in a loop, will be cached.
v8 optimises code on the fly by observing what its actually doing, and how often
v8 will identify the objects with the same set of named properties, added in the same order (like a class constructor would do, or a repetitive bit of JSON, and handle them in the same way.
see the article for more details, then apply at google for a job :)
I am aware of being able to use typeof, however, i would like to know if using
String(anyVariable) === anyVariable
in order to figure out if anyVariable is a string:
Is a generally valid approach?
Works consistently among browsers?
Has any pitfalls?
I would say do not do that, and use typeof because "String" is used to manipulate a stored piece of text, not compare types. It is best to use the features in their intended use, to assure the most stability, and best practice out of it. Also, the purpose is to extend the type with methods. So you are basically causing more work and processing, instead of just a type comparison. Hopefully that answers it, though this is a question that merely has an "opinion" as an answer. You wouldn't create a new object, assign it to your current object, to check if it is a type of object would you? No, you would just use "typeof".
I can think of no reason to use your method vs. the much simpler typeof. Yours is likely to perform worse (15x slower by Matti's jsperf) and be more complex.
Your method is going to require multiple memory manipulations (creating string object, then assign string value to it) and then need to run the garbage collector afterwards whereas typeof just looks at a property of the internal javascript object.
When in doubt, choose the simplest method that solves your problem.
When in doubt, choose the method that is specified in the language definition for solving your problem.
When in doubt, choose the method that requires less memory manipulation.
EDIT: To be explicit, I am not looking for advice or opinions on the qualitative merit of the various issues implied by the functionality in question — neither am I looking for a reliable solution to a practical problem; I am simply looking for technical, verifiable answers to the question in the title. I have appended the question with a list of non-conforming browsers.
Using a function's .toString method will typically render the source code for that function. The problem is that this behaviour isn't specified — the spec refrains from making any commitment as to what the behaviour should be when applied to functions. Chrome's console will even tell you (when you pass anything other than a function to Function.toString.call), that Function.prototype.toString is not generic
This blog post suggests this can be used as a method to produce a readable syntax for multi-line strings (by storing the string as a multi-line comment in the body of a no-op function). The author suggests this usage in the context of writing Node.js applications with the clause that this behaviour is only reliable because Node.js runs in a controlled environment. But in Javascript's native web, anything can come along and interpret it, and we shouldn't rely on unspecified behaviour.
In practice though, I've set up a fiddle which renders a select box whose contents are determined by a large multi-line string to test the code, and every browser on my workstation (Chrome 27, Firefox 21, Opera 12, Safari 5, Internet Explorer 8) executes as intended.
What current Javascript engines don't behave as follows?
Given that:
function uncomment(fn){
return fn.toString().split(/\/\*\n|\n\*\//g).slice(1,-1).join();
}
The following:
uncomment(function(){/*
erg
arg
*/});
Should output:
erg
arg
List of non-conforming browsers:
Firefox 16
…
What current Javascript engines don't behave this way?
Your question isn't really well-defined, given that you haven't defined "popular". Is IE6 popular? IE5? IE4? Netscape Navigator? Lynx? The only way to properly answer your question is to enumerate which browsers you wish to support and check them. Unfortunately kangax's table http://kangax.github.io/es5-compat-table/# doesn't test Function.prototype.toString
Chrome's console will even tell you (when you pass anything other than a function o Function.toString.call), that Function.prototype.toString is not generic
mandated in the spec
the spec refrains from making any commitment as to what the behaviour should be when applied to functions
The required behavior is specified in ECMA-262 version 1 (from 1997, http://www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-262,%201st%20edition,%20June%201997.pdf) You have to chase it down:
http://www.ecma-international.org/ecma-262/5.1/#sec-4.3.24 "function ... member of the Object type that is an instance of the standard built-in Function constructor and that may be invoked as a subroutine"
From that, we deduce that functions are objects.
http://www.ecma-international.org/ecma-262/5.1/#sec-9.8 "Let primValue be ToPrimitive(input argument, hint String)."
So now what is ToPrimitive?
http://www.ecma-international.org/ecma-262/5.1/#sec-9.1 " The default value of an object is retrieved by calling the [[DefaultValue]] internal method of the object, passing the optional hint PreferredType. "
So we need to know what DefaultValue does
http://www.ecma-international.org/ecma-262/5.1/#sec-8.12.8 (lots of words that basically say if the thing has a toString method, then call it)
Now we just need to find where Function.prototype.toString is described:
http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.4.2 "An implementation-dependent representation of the function is returned. This representation has the syntax of a FunctionDeclaration. Note in particular that the use and placement of white space, line terminators, and semicolons within the representation String is implementation-dependent."
So you are guaranteed that you get a proper javascript representation (not some IL gobbledegook) but not necessarily with the comments. For example, the technique breaks in Firefox 16 (but then you have to ask if it is current).
So Kangax has returned to the subject matter (intrigued as he was by the fact that Angular uses this hack for core functionality in client-side code) and written up an analysis of the practice, and produced a test table for the state of function decompilation in Javascript.
The takeaway points are that:
The technique is only remotely reliable for user-defined function declarations.
Some old mobile browsers will still collapse functional code, allegedly for performance reasons.
Other old browsers will reveal optimized code, something like what you might get out of Closure Compiler.
Yet others will remove comments and alter whitespace.
Internet Explorer will occasionally add comments and whitespace around the functions.
The AngularJS team seem to think this technique is robust enough to include in their library without explicit caveat. They then tokenize (!) the code and re-evaluate it (!!).
For my purposes, this makes me reasonably confident I can do something relatively undemanding like detect whether a function has an uppercase name or not by parsing it as follows:
/function\s*[A-Z]/.test( fn )
Objects in JavaScript can be used as Hashtable
(the key must be String)
Is it perform well as Hashtable the data structure?
I mean , does it implemented as Hashtable behind the scene?
Update: (1) I changed HashMap to hashtable (2) I guess most of the browser implement it the same, if not why not? is there any requirement how to implement it in the ECMAScript specs?
Update 2 : I understand, I just wonder how V8 and the Firefox JS VM implements the Object.properties getters/setters?
V8 doesn't implement Object properties access as hashtable, it actually implement it in a better way (performance wise)
So how does it work? "V8 does not use dynamic lookup to access properties. Instead, V8 dynamically creates hidden classes behind the scenes" - that make the access to properties almost as fast as accessing properties of C++ objects.
Why? because in fixed class each property can be found on a specific fixed offset location..
So in general accessing property of an object in V8 is faster than Hashtable..
I'm not sure how it works on other VMs
More info can be found here: https://v8.dev/blog/fast-properties
You can also read more regarding Hashtable in JS here:(my blog) http://simplenotions.wordpress.com/2011/07/05/javascript-hashtable/
"I guess most of the browser implement it the same, if not why not? is there any requirement how to implement it in the ECMAScript specs?"
I am no expert, but I can't think of any reason why a language spec would detail exactly how its features must be implemented internally. Such a constraint would have absolutely no purpose, since it does not impact the functioning of the language in any way other than performance.
In fact, this is absolutely correct, and is in fact the implementation-independence of the ECMA-262 spec is specifically described in section 8.6.2 of the spec:
"The descriptions in these tables indicate their behaviour for native
ECMAScript objects, unless stated otherwise in this document for particular kinds of native ECMAScript objects. Host objects may support these internal properties with any implementation-dependent behaviour as long as it is consistent with the specific host object restrictions stated in this document"
"Host objects may implement these internal methods in any manner unless specified otherwise;"
The word "hash" appears nowhere in the entire ECMA-262 specification.
(original, continued)
The implementations of JavaScript in, say, Internet Explorer 6.0 and Google Chrome's V8 have almost nothing in common, but (more or less) both conform to the same spec.
If you want to know how a specific JavaScript interpreter does something, you should research that engine specifically.
Hashtables are an efficient way to create cross references. They are not the only way. Some engines may optimize the storage for small sets (for which the overhead of a hashtable may be less efficient) for example.
At the end of the day, all you need to know is, they work. There may be faster ways to create lookup tables of large sets, using ajax, or even in memory. For example see the interesting discussion on this post from John Reseig's blog about using a trie data structure.
But that's neither here nor there. Your choice of whether to use this, or native JS objects, should not be driven by information about how JS implements objects. It should be driven only by performance comparison: how does each method scale. This is information you will get by doing performance tests, not by just knowing something about the JS engine implementation.
Most modern JS engines use pretty similar technique to speed up the object property access. The technique is based on so called hidden classes, or shapes. It's important to understand how this optimization works to write efficient JS code.
JS object looks like a dictionary, so why not use one to store the properties? Hash table has O(1) access complexity, it looks like a good solution. Actually, first JS engines have implemented objects this way. But in static typed languages, like C++ or Java a class instance property access is lightning fast. In such languages a class instance is just a segment of memory, end every property has its own constant offset, so to get the property value we just need to take the instance pointer and add the offset to it. In other words, in compile time an expression like this point.x is just replaced by its address in memory.
May be we can implement some similar technique in JS? But how? Let's look at a simple JS function:
function getX(point) {
return point.x;
}
How to get the point.x value? The first problem here is that we don't have a class (or shape) which describes the point. But we can calculate one, that is what modern JS engines do. Most of JS objects at runtime have a shape which is bound to the object. The shape describes properties of the object and where these properties values are stored. It's very similar to how a class definition describes the class in C++ or Java. It's a pretty big question, how the Shape of an object is calculated, I won't describe it here. I recommend this article which contains a great explanation of the shapes in general, and this post which explains how the things are implemented in V8. The most important thing you should know about the shapes is that all objects with the same properties which are added in the same order will have the same shape. There are few exceptions, for example if an object has a lot of properties which are frequently changed, or if you delete some of the object properties using delete operator, the object will be switched into dictionary mode and won't have a shape.
Now, let's imagine that the point object has an array of property values, and we have a shape attached to it, which describes where the x value in this property array is stored. But there is another problem - we can pass any object to the function, it's not even necessary that the object has the x property. This problem is solved by the technique called Inline caching. It's pretty simple, when getX() is executed the first time, it remembers the shape of the point and the result of the x lookup. When the function is called second time, it compares the shape of the point with the previous one. If the shape matches no lookup is required, we can take the previous lookup result.
The primary takeaway is that all objects which describe the same thing should have the same shape, i.e. they should have the same set of properties which are added in the same order. It also explains why it's better to always initialize object properties, even if they are undefined by default, here is a great explanation of the problem.
Relative resources:
JavaScript engine fundamentals: Shapes and Inline Caches and a YouTube video
A tour of V8: object representation
Fast properties in V8
JavaScript Engines Hidden Classes (and Why You Should Keep Them in Mind)
Should I put default values of attributes on the prototype to save space?
this article explains how they are implemented in V8, the engine used by Node.js and most versions of Google Chrome
https://v8.dev/blog/fast-properties
apparently the "tactic" can change over time, depending on the number of properties, going from an array of named values to a dictionary.
v8 also takes the type into account, a number or string will not be treated in the same way as an object (or function, a type of object)
if i understand this correctly a property access frequently, for example in a loop, will be cached.
v8 optimises code on the fly by observing what its actually doing, and how often
v8 will identify the objects with the same set of named properties, added in the same order (like a class constructor would do, or a repetitive bit of JSON, and handle them in the same way.
see the article for more details, then apply at google for a job :)