Reputation: 554
please help :) — I had to check performance of accessing properties — Factory function, Concatenative inheritance and class and I don’t understand why Concatenative inheritance loses? After all, access to the copied properties in Concatenative inheritance should be the fastest. I run test on Chrome and Safari and CI always loses. https://jsperf.com/factory-function-vs-concatenative-inheritance-get
Code from this test:
const alien = {
sayHello () {
return `Hello, my name is ${ this.name }`;
}
};
//Delegate prototype
const createAlienFF = (name) => {
return Object.assign(Object.create(alien), {
name
});
}
//Delegate prototype - creating objects
var tabFF = [];
for(var i = 0; i < 1000; i++ ) {
tabFF[i] = createAlienFF('Clark' + i);
}
//Concatenative inheritance
const createAlienCI = (name) => {
return Object.assign(
{},
alien,
{name: name}
);
}
//Concatenative inheritance - creating objects
var tabCI = [];
for(var i = 0; i < 1000; i++ ) {
tabCI[i] = createAlienCI("Clark" + i );
}
//Class
class Alien {
constructor(name) {
this.name = name
}
sayHello() {
return `Hello, my name is ${ this.name }`;
}
}
//Class - creating objects
var tabClass = [];
for(var i = 0; i < 1000; i++ ) {
tabClass[i] = new Alien("Clark" + i );
}
//Tests
//1 - Delegate prototype
for(var i = 0; i < 1000; i++ ) {
tabFF[i].sayHello();
}
//2 - Concatenative inheritance
for(var i = 0; i < 1000; i++ ) {
tabCI[i].sayHello();
}
//3 - Class
for(var i = 0; i < 1000; i++ ) {
tabClass[i].sayHello();
}
Upvotes: 2
Views: 271
Reputation: 664548
After all, access to the copied properties in Concatenative inheritance should be the fastest.
Why do you think that? Because it's a direct property access that does not need to traverse the prototype chain?
Well yeah, that might be a good reasoning about a naive engine implementation. But you didn't reckon with the optimisations of the object model that is specifically tailored towards inheritance, making method access on (class) instances blazingly fast. Have a look at https://github.com/v8/v8/wiki/Design%20Elements#fast-property-access, https://blog.ghaiklor.com/optimizations-tricks-in-v8-d284b6c8b183, http://richardartoul.github.io/jekyll/update/2015/04/26/hidden-classes.html and the great http://mrale.ph/blog/2012/06/03/explaining-js-vms-in-js-inline-caches.html. The gist: V8 optimises the method access on objects of "known shape", that is the objects inheriting from alien
or Alien.prototype
in your case (they share the same hidden class). It knows guesses that in your loop, only objects of that shape are used, and can deduce that all of them call the exact same sayHello
function. That's a constant location to fetch the code from, enabling even further optimisations such as inlining. On the other hand, the objects created by the createAlienCI
factory do as well all share the same shape, but each object contains its individual sayHello
property. All these properties might contain the same function object, but we cannot know that (and we wouldn't guess it - it's an unusual pattern). The engine therefore fetches the function reference from each instance every time before calling it - at least it knows where (at which memory offset) in each instance to look because of their constant shape.
Upvotes: 1