In Chrome's JavaScript console:
> function create(proto) {
function Created() {}
Created.prototype = proto
return new Created
}
undefined
> cc = create()
Created {}
> cc
Created {}
Created is a function private to the create function; after create completes, there are no (known to me) references to Created. Yet Chrome can show the function's name at any time, starting from the object created by it.
Chrome didn't achieve this by following the "naïve" approach:
> cc.constructor
function Object() { [native code] }
> cc.toString()
"object [Object]"
and anyway, I didn't set constructor on the proto argument passed to create:
> cc.__proto__.hasOwnProperty("constructor")
false
One guess I had is that the JavaScript VM holds on to Created for the sake of the instanceof mechanism. It is said that instanceof
tests whether an object has in its prototype chain the prototype property of a constructor.
But in the above code I typed create(), effectively passing undefined as prototype; consequently Created doesn't even have its prototype set to the actual cc.__proto__. We can verify this if we hack create to expose the Created function:
function create(proto) {
function Created() {}
Created.prototype = proto
GlobalCreated = Created
return new Created
}
now let's type
> cc = create()
Created {}
> GlobalCreated
function Created() {}
> GlobalCreated.prototype
undefined
> cc instanceof GlobalCreated
TypeError: Function has non-object prototype 'undefined' in instanceof check
My questions (all closely related):
What exactly does Chrome's JavaScript engine retain to make that object presentation in the console work? Is it the constructor function, or just the function name?
Is that retention needed for anything more substantial than console printout?
What is the effect of such retention on memory consumption? What if, for example, the constructor function (or even its name) is abnormally huge?
Is it just Chrome? I've retested with Firebug and Safari, their consoles don't present the object that way. But do they still retain the same data, for other possible purposes (e.g. due to a genuine concern inherent to a JavaScript VM)?
Late edit:
I recently revisited this question/answer, and I think I've figured out why chrome seems to "hang on" to the Created name. It's not really something that is exclusive to V8, but I think it's the result of how V8 works behind the scenes (the hidden objects I explained in my initial answer), and what V8 is required to do (to conform to the ECMAScript standard).
Any function, constructor functions or otherwise, share the same constructor and prototype-chain by default:
function Created(){};
console.log(Created.constructor);//function Function() { [native code] }
console.log(Object.getPrototypeOf(Created));//function Empty() {}
console.log(Created.__proto__);//same as above
console.log(Created.prototype);//Created {}
This tells us a few things: All functions share the native Function constructor, and inherit from a specific function instance (function Empty(){}) that is used as their prototype. However, a function's prototype property is required to be an object, that the function would return if it were called as a constructor (see ECMAScript standard).
The value of the prototype property is used to initialise the [[Prototype]] internal property of a newly created object before the Function object is invoked as a constructor for that newly created object. This property has the attribute { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }.
We can verify this easily by looking at the Created.prototype.constructor:
console.log(Created.prototype.constructor);//function Created() {}
Now let's, for a moment, list the hidden classes V8 needs to, and probably will, create in order for it to comply to the standard:
function Created(){}
Hidden classes:
Object, of course: the mother of all objects, of which Function is a specific child
Function: This native object is, as we've demonstrated, the constructor
function Empty: The prototype, from which our function will inherit
Created our empty function that will inherit from all of the above
At this stage, nothing unusual has happened, and it's self-evident that, when we return an instance of this Created constructor, the Created function will get exposed because of its prototype.
Now, because we're reassigning the prototype property you could argue that this instance will be discarded, and is lost, but from what I understand, that's not how V8 will handle this situation. Instead, it'll create an additional hidden class, that simply overrides the prototype property of its parent after this statement is encountered:
Created.prototype = proto;
Its internal structure will end up looking something like this (numbered this time, because I'll refer back to certain stages within this inheritance chain further down):
Object, of course: the mother of all objects, of which Function is a specific child
Function: This native object is, as we've demonstrated, the constructor
function Empty: The prototype, from which our function will inherit
Created our empty function that will inherit from all of the above
Created2: extends the previous class (Created), and overrides prototype
So why is Created still visible?
That's the million dollar question, to which I think I have the answer now: Optimization
V8 simply can't, nor should it be allowed to, optimize out the Created hidden class (stage 4). Why? Because what will override prototype is an argument. It's something that can't be predicted. What V8 will probably do to optimize the code is to store a hidden object 4, and whenever the create function is called, it'll create a new hidden class that extends stage 4, overriding the prototype property with whatever value is passed to the function.
Because of this, Created.prototype will always exist somewhere inside each instance's internal representation. It's also important to note you could replace the prototype property with one that actually referenced an instance of Created (with a mucked-up prototype chain, but still):
cc = create();
console.log(Object.getPrototypeOf(cc))//Object {}
cc = create(new GlobalCreated);
console.log(Object.getPrototypeOf(cc));//Created {}
How's that for a mind-bender? Inception script-writers, eat your hearts out...
Anyway, I hope all of this dribble made some sense to someone out here, if not, I do respond to comments, so corrections to mistakes I may have made, or questions regarding some part of this update that is a bit unclear are welcome...
I'll try to answer question by question, but as you say, they're all closely related, so the answers overlap up to a point.
While reading this, bare in mind that I wrote this in one go, whilst feeling a bit feverish. I am not a V8 expert, and based this on recollections of my doing some digging in the V8 internals some time ago. The link at the bottom is to the official docs, and will of course contain more accurate and up-to-date information on the subject.
What is going on
What chrome's V8 engine actually does is create a hidden class for each object, and this class is mapped to the JS representation of the object.
Or as the people at google say so themselves:
To reduce the time required to access JavaScript properties, V8 does not use dynamic lookup to access properties. Instead, V8 dynamically creates hidden classes behind the scenes.
What happens in your case, extending, creating a new constructor from a particular instance and overriding the constructor property is actually nothing more than what you can see on this graph:
Where hidden class C0 could be regarded as the standard Object class. Basically, V8 interprets your code, builds a set of C++ like classes, and creates an instance if needed. The JS objects you have are set to point to the different instances whenever you change/add a property.
In your create function, this is -very likely- what is going on:
function create(proto)
{//^ creates a new instance of the Function class -> cf 1 in list below
function Created(){};//<- new instance of Created hidden class, which extends Function cf 2
function Created.prototype = proto;//<- assigns property to Created instance
return new Created;//<- create new instance, cf 3 for details
}
Right: Function is a native construct. The way V8 works means that there is a Function class that is referenced by all functions. They reference this class indirectly, though, because each function has its own specifcs, which are specified in a derived hidden class. create, then, should be seen as a reference to create extends HiddenFunction class.
Or, if you wish, in C++ syntax: class create : public Hidden::Function{/*specifics here*/}
The Create function references a hidden function identical to create. However, after declaring it, the class receives 1 propriety property, called prototype, so another hidden class is created, specifying this property. This is the basis of your constructor. Because the function body of create, where all of this happens, this is a given, and V8 will probably be clever enough to create these classes beforehand, anyway: in C++ pseudo-code, it'll look similar to code listing 1 below.
Each function call will assign a reference to a new instance Of the hidden class described above, to the Created name, which is local to create's scope. Of course, the returned instance of create does still retain the reference to this instance, but that's how JS scopes work, and so this applies to all engines... think of closures and you'll get what I mean (I'm really struggling with this nasty fever... sorry to nag about this)
At this stage Create points to an instance of this hidden class, which extends a class that extends a class (as I tried to explain in point 2). Using the new keyword triggers behaviour defined by the Function class, of course (as it's a JS language construct). This results in a hidden class to be created which is probably the same for all instances: it extends the native object, and this has a constructor property, which references the instance of Created we've just made. The instances returned by create though are all alike. Sure their constructors may have a different prototype property, but the objects they churn out all look the same. I'm fairly confident that V8 will only create 1 hidden class for the objects create returns. I can't see why the instances should require different hidden classes: their property names & count are the same, but each instance references another instance, but that's what classes are for
Anyway: code listing for item 2, a pseudo-code representation of what Created might look like in hidden-class terms:
//What a basic Function implementation might look like
namespace Hidden
{//"native" JS types
class Function : public Object
{
//implement new keyword for constructors, differs from Object
public:
Function(...);//constructor, function body etc...
Object * operator new ( const Function &);//JS references are more like pointers
int length;//functions have a magic length property
std::string name;
}
}
namespace Script
{//here we create classes for current script
class H_create : public Hidden::Function
{};
class H_Created : public Hidden::Function
{};//just a function
class H_Created_with_prototype : public H_Created
{//after declaring/creating a Created function, we add a property
//so V8 will create a hidden class. Optimizations may result in this class
// being the only one created, leaving out the H_Created class
public:
Hidden::Object prototype;
}
class H_create_returnVal : public Hidden::Object
{
public:
//the constructor receives the instance used as constructor
//which may be different for each instance of this value
H_create_returnVal(H_Created_with_prototype &use_proto);
}
}
Ignore any (likely) syntax oddities (it's been over a year since I wrote a line of C++), and ignoring namespaces and wacky names, The listed classes are, apart from the Hidden::Function effectively all the hidden classes that will ever need to be created to run your code. All your code then does is assign references to instances of these classes. The classes themselves don't take up much space in memory. And any other engine will create just as many objects, because they, too, need to comply with the ECMAScript specs.
So I guess, looking at it like this, this sort of answers all your questions: no not all engines work like this, but this approach won't cause massive amounts of memory to be used, Yes, this does mean a lot of information/data/references to all objects is retained, but that's just an unavoidable, and in some cases happy side-effect of this approach.
Update: I did a bit more digging, and found an example of how you could add JS functions to V8 using templates, it illustrates how V8 translates JS objects/functions to C++ classes, see the example here
This is just me speculating, but I wouldn't at all be surprized to learn that the way V8 works, and this retention business is heavily used in garbage-collection and memory management in general (EG: deleting a property changing hidden classes and the like)
For example:
var foo = {};//foo points to hidden class Object instance (call id C0)
foo.bar = 123;//foo points to child of Object, which has a property bar (C1)
foo.zar = 'new';//foo points to child of C1, with property zar (C2)
delete foo.zar;//C2 level is no longer required, foo points to C1 again
That last bit is just me guessing, but it could be possible for the GC to do this.
What is this retention used for
As I said, in V8, a JS object is actually a sort-of pointer to a C++ class. Accessing properties (and this includes the magic properties of arrays, too!), is fast. Really, really fast. In theory, accessing a property is an O(1) operation.
That's why, on IE:
var i,j;
for(i=0,j=arr.length;i<j;++i) arr[i] += j;
Is faster than:
for (i=0;i<arr.length;++i) arr[i] += arr.length;
While on chrome, arr.length is faster as shown her. I also answered that question, and it, too, contains some details on V8 you may want to check. It could be that my answer there doesn't (completely) apply anymore, because browsers and their engines change fast...
What about the memory
Not a big problem. Yes, Chrome can be a bit of resource hog at times, but the JS isn't always to blame. Write clean code, and the memory footprint won't be too different on most browsers.
If you create a huge constructor, then V8 will create a larger hidden class, but if that class specifies a lot of properties already, then chances of their being a need for additional hidden classes is smaller.
And of course, each function is an instance of the Function class. This being a native (and very important) type in a functional language will most likely be a highly optimized class anyway.
Anyway: as far as memory usage is concerned: V8 does a pretty good job managing memory. Far better than IE's of old, for example. So much so that the V8 engine is used for server-side JS (as in node.js), if memory really was an issue, then you wouldn't dream of running V8 on a server that must be up and running as much as possible, now would you?
Is this just Chrome
Yes, in a way. V8 does have a special take on how it consumes and runs JS. Rather than JIT-compiling your code to bytecode and running that, it compiles the AST straight into machine code. Again, like the hidden-classes trickery, this is to increase performance.
I know I included this graph in my answer on CR, but just for completeness' sake: Here's a graph that shows the differences between chrome (bottom) and other JS engines (top)
Notice that below the bytecode instructions and the CPU, there's an (orange) interpreter layer. That's what V8 doesn't need, owing to the JS being translated into machine code directly.
The downside being that this makes certain optimizations harder to do, especially concerning the ones where DOM data and user input is being used in the code (for example: someObject[document.getElementById('inputField').value]) and that the initial processing of the code is harder on the CPU.
The upside is: once the code is compiled into machine code, it's the fastest you're going to get, and running the code is likely to cause less overhead. A bytecode interpreter is heavier on the CPU most of the time, that's why busy loops on FF and IE can cause the browser to alert the user of a "running script" asking them if they want to stop it.
more on V8 internals here
I don't know much about Chrome's internals, so this is just a guess, but it seems to me that Chrome is performing some kind of static analysis on the code which created the function, and storing that for debugging purposes.
Take a look at this example:
> function create(proto) {
object = {}
object.x = {}
x = object.x
x.func = function() {}
x.func.prototype = proto
return new object.x.func
}
undefined
> create()
x.func {}
x.func? There's no way JavaScript has any built-in way for you to access the name of the variable a function was initially assigned to. Chrome must be doing that for its own reasons.
Now look at this example:
> function newFunc() {
return function() {}
}
> function create(proto) {
object = {}
object.x = {}
x = object.x
x.func = newFunc()
x.func.prototype = proto
return new object.x.func
}
undefined
> create()
Object {}
In this example, since we created the function in a separate closure before assigning it to a variable, Chrome doesn't know the "name" of the function, so it just says "Object".
These examples lead me to guess the following answers to your questions:
What exactly does Chrome's JavaScript engine retain to make that object presentation in the console work? Is it the constructor function, or just the function name?
It performs a static analysis of the code, and stores a string containing the function's "name" somewhere.
Is that retention needed for anything more substantial than console printout?
Probably not.
What is the effect of such retention on memory consumption? What if, for example, the constructor function (or even its name) is abnormally huge?
I'm not sure, but I'm guessing it's very unlikely to be an issue. Since the name of the function is determined using static analysis, the potential size of the function name is limited by the size of variable names in the script which created it (unless perhaps you're using eval, in which case I'm not sure).
Is it just Chrome? I've retested with Firebug and Safari, their consoles don't present the object that way. But do they still retain the same data, for other possible purposes (e.g. due to a genuine concern inherent to a JavaScript VM)?
I doubt it, this seems to be something specific to Chrome used to make debugging a bit easier. As far as I can tell, there's no other reason for a feature like this to exist.
Disclaimer: I am not a Google Chrome expert, however I think that these are not browser-specific, and can be explained by basic Javascript rules.
What exactly does Chrome's JavaScript engine retain to make that
object presentation in the console work? Is it the constructor
function, or just the function name?
Each Object or Function in Javascript has its inheritance chain, going up, all the way to the basic prototype.
You can not circumvent this by setting the prototype property to undefined, although it may seem like it from the console output.
So it is the whole constructor function that is retained because of inheritance, although not available to be accessed through global scope.
Is that retention needed for anything more substantial than console
printout?
Yes, it is needed for the prototype inheritance system to work.
What is the effect of such retention on memory consumption? What if,
for example, the constructor function (or even its name) is abnormally
huge?
Yes, this can cause a memory leak if used improperly.
This is why you should always delete and clean unused variables, so these and their prototypes can get collected by the garbage collector.
Is it just Chrome? I've retested with Firebug and Safari, their
consoles don't present the object that way. But do they still retain
the same data, for other possible purposes (e.g. due to a genuine
concern inherent to a JavaScript VM)?
This should work the same way across all browsers, because prototypal inheritance works the same. I have however not specifically tested for it. Please note that the console outputs int browsers can differ, and this does not mean anything, as each browser can implement its console in its own way.
//The real method to do clone
function doClone(source, keys, values, result) {
if (source == null || typeof (source) !== "object") {
return source;
}
if (source.Clone instanceof Function) {
return source.Clone();
}
if (source instanceof Date) {
if (!(result instanceof Date)) {
result = new Date();
}
result.setTime(source.getTime());
return result;
}
else if (source instanceof Array) {
if (!(result instanceof Array)) {
result = [];
}
for (var i = 0; i < source.length; i++) {
result[i] = clone(source[i], keys, values, result[i]);
}
return result;
}
try {
if (typeof result !== "object" || result == null) {
result = new source.constructor();
} else {
result.constructor = source.constructor;
}
if (source.prototype) {
result.prototype = source.prototype;
}
if (source.__proto__) {
result.__proto__ = source.__proto__;
}
} catch (e) {
if (Object.create) {
result = Object.create(source.constructor.prototype);
} else {
result = {};
}
}
if (result != null) {
// ReSharper disable once MissingHasOwnPropertyInForeach
for (var property in source) {
if (source.hasOwnProperty(property)) {
try {
var descriptor = Object.getOwnPropertyDescriptor(source, property);
if (descriptor != null) {
if (descriptor.get || descriptor.set) {
Object.defineProperty(result, property, descriptor);
} else {
descriptor.value = clone(descriptor.value, keys, values, result[property]);
Object.defineProperty(result, property, descriptor);
}
} else {
result[property] = clone(source[property], keys, values, result[property]);
}
} catch (e) {
result[property] = clone(source[property], keys, values, result[property]);
}
}
}
}
return result;
}
//The portal of clone method
function clone(source, keys, values, result) {
var index = keys.indexOf(source);
if (index !== -1) {
return values[index];
}
result = doClone(source, keys, values, result);
index = keys.indexOf(source);
if (index !== -1) {
values[index] = result;
} else {
keys.push(source);
values.push(result);
}
return result;
}
/**
* Core functions
*/
var X = {
/**
* Clone indicated source instance
* #param {} source The source instance to be clone
* #param {} target If indicated, copy source instance to target instance.
* #returns {}
*/
Clone: function (source, target) {
return clone(source, [], [], target);
}
}
You return a new instance from create to a object called Created.
create()()
> TypeError: object is not a function
If you were to remove the 'new' keyword, then you would expose the Created function to the caller's scope.
Related
The MDN gives the following working example of Symbol.species:
class MyArray extends Array {
// Overwrite species to the parent Array constructor
static get [Symbol.species]() { return Array; }
}
var a = new MyArray(1,2,3);
var mapped = a.map(x => x * x);
console.log(mapped instanceof MyArray); // false
console.log(mapped instanceof Array); // true
The ECMAScript 2015 specifications says:
A function valued property that is the constructor function that is used to create derived objects.
The way I understand it it would be mainly used to Duck-Type in some way custom objects in order to trick the instanceof operator.
It seems very useful but I have not managed to use it on plain objects (FF 44.0a2):
let obj = {
get [Symbol.species]() {
return Array
}
}
console.log(obj instanceof Array) //false =(
console.log(obj instanceof Object) //true
Is there any way to use Symbol.species on plain objects in order to trick the instanceof operator?
The way I understand it it would be mainly used to Duck-Type in some way custom objects in order to trick the instanceof operator.
Nope, that's what Symbol.hasInstance is good for (though it makes tricky constructors, e.g. for mixins, not tricky instances).
The point of Symbol.species is to let built-in methods determine the proper type for derived objects. Whenever a function is supposed to return a new instance of the same kind, it usually instantiates a new this.constructor, which might be a subclass of the class that defined the method. But you might not always want this when subclassing, and that's where Symbol.species comes in.
The example given by MDN is quite good, you just must not miss the distinction between a and mapped:
var a = new MyArray(1,2,3);
var mapped = a.map(x => x * x);
Object.getPrototypeOf(a) == MyArray.prototype; // true
Object.getPrototypeOf(mapped) == Array.prototype; // true
(As you know, instanceof is was just sugar for the reverse of isPrototypeOf)
So what happens here is that the Array map method is called on an instance of MyArray, and creates a derived object that is now an instance of Array - because a.constructor[Symbol.species] said so. Without it, map would have created another MyArray.
This trick would work with plain objects just as well:
var b = {
length: 0,
map: Array.prototype.map,
constructor: { // yes, usually a function
[Symbol.species]: MyArray
}
};
var mapped = b.map(x => x*x);
console.log(mapped instanceof MyArray); // true
I can't say whether it's any useful in this example, though :-)
Good question. I admit that I'm not 100% sure, but I've gotten the impression that the answer is no.
I used two methods to determine this answer:
Finding an environment that clearly supports all of the features necessary to test this, and running the code in that environment directly
Referencing the spec
The first point wasn't easy to test, and I wasn't entirely sure that I successfully did it. You used Firefox 44, but I decided to test another environment due to the fact that Kangax's support table says it only supports the existence of Symbol.species, and none of the Array-like features. To see this, you can expand the Symbol.species section in the link above.
In this case, even Kangax is a bit confusing. I would expect there to be non-Array specific tests for Symbol.species aside from the existence test, but maybe I'm just not knowledge enough to understand that those tests cover all of the functionality of Symbols. I'm not sure!
While browsing the table, I saw that Edge supports all of the Symbol.species features. I loaded up my Windows VM, hopped on over to Edge 20, and pasted your code.
In Edge 20, the code had the same result as your test in Firefox 44.
So this began to give me confidence that the feature doesn't work in the way that you want it to. Just to make sure that Edge had its JavaScript in order, I decided to run the code from the MDN article you linked, and...I got a syntax error! It appears Edge is still updating its Class implementation due to late-in-the-game changes to the Class spec, so it's not enabled.
Their current implementation is, however, available through an experimental flag. So I flipped that on and was able to reproduce the result that MDN got.
And that was the extent of my JavaScript environment testing. I don't have 100% confidence in this test, but I would say that I'm fairly confidence in the result.
So onto the second method I used to determine my result: the specification.
I admit that I'm no expert at the ECMAScript specification. I do my best to understand it, but it is likely that I might misinterpret some of the information in there. The reason for this is because I'm not an ECMAScript master, so some of the technical jargon used is just way over my head.
Nevertheless, I can sometimes get the gist of what is being said. Here's a snippet that's about the Symbol ##species:
Methods that create derived collection objects should call ##species to determine the constructor to use to create the derived objects. Subclass constructor may over-ride ##species to change the default constructor assignment.
The phrase "derived objects" is used quite frequently in describing this symbol, but it's not defined anywhere. One might interpret it to be a synonym of sorts with instantiated. If that's a correct interpretation, then this can likely only be used with constructors or classes.
Either way, it sounds as if ##species is an active Symbol, rather than a passive one. By this I mean that it sounds like it's a function that's actually called during the creation of an Object, which is then what determines the instanceof value, rather than a function that's called at the time of instanceof being called.
But anyway, take that interpretation with a grain of salt. Once again, I'm not 100% certain of any of this. I considered posting this as a comment rather than an answer, but it ended up getting quite long.
I have a Nashorn engine in which I evaluate some scripts that expose some common utility functions and objects. I want custom scripts to run in their own contexts and not step over each other, so I create new contexts for them using engine.createBindings():
ScriptContext newContext = new SimpleScriptContext();
newContext.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
newContext.getBindings(ScriptContext.ENGINE_SCOPE).putAll(engine.getBindings(ScriptContext.ENGINE_SCOPE));
Now I have access to everything that was created in the original scope, but this also creates an entirely-new global-object for the new context, which means that instances of native JS objects like Object, Number, etc. are different from corresponding instances in the original context.
This leads to some strange behavior. For example, assume you have the following code that was evaluated in the engine (i.e., the "parent" context"):
function foo(obj) {
print(JSON.stringify(obj, null, 4));
print(Object.getPrototypeOf(obj) === Object.prototype);
}
Now let's say your custom script is as follows:
function bar() {
foo({a: 10, b: 20});
}
I evaluate this against newContext and then invoke the function:
engine.eval(source, newContext);
ScriptObjectMirror foo = newContext.getAttribute("foo", ScriptContext.ENGINE_SCOPE);
foo.call(null);
This returns:
undefined
false
This is expected behavior because objects created in other contexts are treated as foreign objects.
What I'm trying to do is to expose a common library of functions and maintain that within a single script-engine instance. I don't want to keep recreating script-engine instances because I end up losing JIT optimizations (I read this somewhere, but I can't find the link right now). I do like the fact that objects "remember" their originating global-context, but I'd like that not to happen in the case of native JS objects.
Is there a way to create an entirely-new global context, while still sharing JS global-object instances? I've tried manually copying over these instances (enumerating the properties of this), but when I copy them over to the new context, they are ScriptObjectMirror instances and not the unwrapped versions. I assume this is because they were originally created in a different context and therefore are considered to be "foreign".
It looks like it is not possible to do this unfortunately. I even unwrapped the objects and got back the native objects (I did this from the main thread), and then overwrote the ones in the new context. Unfortunately, this still doesn't work since the Java classes that represent native objects in Java maintain internal references to the original instances that were created (for prototypes of the object).
My workaround for the two cases above was to do this for the object test:
var proto = Object.getPrototypeOf(object);
return (typeof proto.constructor !== "undefined" && (proto.constructor.name === Object.prototype.constructor.name));
For JSON, I overwrote the native JSON object by evaluating Douglas Crockford's JSON library in the main context, and every new context. I did have to make some modifications to get it to work in Nashorn though. The first was to make it redefine the JSON object without check for its existence first (so just remove the if test). The second was to use the hasOwnProperty from the object itself as well, inside stringify. So the following line:
if (Object.prototype.hasOwnProperty.call(value, k))
Changes to:
if (Object.prototype.hasOwnProperty.call(value, k) || (value.hasOwnProperty && value.hasOwnProperty(k)))
With these two changes I was able to get things to work.
I recently tried to optimize some code for an often created value object. (A three dimensional vector, fwiw)
One thing I tried was to convert the constructor function from an anonymous method factory pattern to a normal JavaScript constructor.
This led to a severe performance penalty which surprised me, since the use of 'new' and normal constructors was much recommended in my last question on the subject of JavaScript constructor/factory patterns.
It could well be that my test is too simplistic, or just plain wrong, or a result of recent performance optimizations made in chrome's JavaScript engine, or all of the above. In any case, I'd really like to know why my 'optimizations' led to performance drop - and - most important: Is there any obvious problem with my jsperf testrun?
The major differences between your tests are:
{} is way faster than new Object, which suggests that new is simply slower than using {}. (The same is true of [] and new Array.)
Your tests produce different results: the result of your make factory function isn't a Make object. The constructed Make has a prototype, shared by all Make objects. Your result of your factory function is just a bare Object, and has a single prototype in its prototype chain (Object.prototype), whereas the Make constructed object has two (Make.prototype, followed by Object.prototype).
I made a fork of your test, with a factory function that actually returns a Make object (instead a simple Object), using the non-standard __proto__ property, and it is much slower than a using a constructor. IE does not support __proto__, but the results from Firefox and Chrome look pretty definitive.
One of the things that a constructor function optimizes for is shared properties, usually methods. If a number of objects use the same functions as methods, or share other named properties, then one assignment to the prototype will share a single instance of the property among all objects created from the constructor, reducing memory overhead, and will not need to repeat the assignment of each such property for every object created, reducing construction time overhead.
Since your sample does not include any such properties, you're not going to see such benefits. But if your production code does not include shared properties for your constructed object, there might well be no reason to switch to a constructor.
So, if, for example you had code like this:
function make(p) {
return {
parm: p,
addTwo: function() {return this.parm + 2;},
double: function() {return this.parm * 2;},
square: function() {return this.parm * this.parm;}
};
};
it will probably run more slowly than this:
function Make(p) {
this.parm = p;
}
Make.prototype.addTwo = function() {return this.parm + 2;};
Make.prototype.double = function() {return this.parm * 2;}
Make.prototype.square = function() {return this.parm * this.parm;}
It will also take a lot more memory if you create many instances.
TL;DR:
Do we need factories/constructors in prototypical OO? Can we make a paradigm switch and drop them completely?
The BackStory:
I've been toying with doing prototypical OO in JavaScript lately and find that 99% of OO done in JavaScript is forcing classical OO patterns into it.
My take on prototypical OO is that it involves two things. A static prototype of methods (and static data) and a data binding. We don't need factories or constructors.
In JavaScript these are Object literals containing functions and Object.create.
This would mean we can model everything as a static blueprint/prototype and a data binding abstraction that's preferably hooked straight into a document-style database. I.e. objects are taken out of the database and created by cloning a prototype with the data. This would mean there is no constructor logic, no factories, no new.
The Example code:
An pseudo example would be :
var Entity = Object.create(EventEmitter, {
addComponent: {
value: function _addComponent(component) {
if (this[component.type] !== undefined) {
this.removeComponent(this[component.type]);
}
_.each(_.functions(component), (function _bind(f) {
component[f] = component[f].bind(this);
}).bind(this));
component.bindEvents();
Object.defineProperty(this, component.type, {
value: component,
configurable: true
});
this.emit("component:add", this, component);
}
},
removeComponent: {
value: function _removeComponent(component) {
component = component.type || component;
delete this[component];
this.emit("component:remove", this, component);
}
}
}
var entity = Object.create(Entity, toProperties(jsonStore.get(id)))
The minor explanation:
The particular code is verbose because ES5 is verbose. Entity above is a blueprint/prototype. Any actual object with data would be created by using Object.create(Entity, {...}).
The actual data (in this case the components) is directly loaded from a JSON store and injected directly into the Object.create call. Of course a similar pattern is applied to creating components and only properties that pass Object.hasOwnProperty are stored in the database.
When an entity is created for the first time it's created with an empty {}
The actual Questions:
Now my actual questions are
Open source examples of JS prototypical OO?
Is this a good idea?
Is it in-line with the ideas and concepts behind prototypical OOP?
Will not using any constructors/factory functions bite me in the ass somewhere? Can we really get away with not using constructors. Are there any limitations using the above methodology where we would need factories to overcome them.
I don't think the constructor/factory logic is necessary at all, as long as you change how you think about Object-Oriented Programming. In my recent exploration of the topic, I've discovered that Prototypical inheritance lends itself more to defining a set of functions that use particular data. This isn't a foreign concept to those trained in classical inheritance, but the hitch is that these "parent" objects don't define the data to be operated on.
var animal = {
walk: function()
{
var i = 0,
s = '';
for (; i < this.legs; i++)
{
s += 'step ';
}
console.log(s);
},
speak: function()
{
console.log(this.favoriteWord);
}
}
var myLion = Object.create(animal);
myLion.legs = 4;
myLion.favoriteWord = 'woof';
So, in the above example, we create the functionality that goes along with an animal, and then create an object that has that functionality, along with the data necessary to complete the actions. This feels uncomfortable and odd to anyone who's used to classical inheritance for any length of time. It has none of the warm fuzziness of the public/private/protected hierarchy of member visibility, and I'll be the first to admit that it makes me nervous.
Also, my first instinct, when I see the above initialization of the myLion object is to create a factory for animals, so I can create lions, and tigers, and bears (oh my) with a simple function call. And, I think, that's a natural way of thinking for most programmers - the verbosity of the above code is ugly, and seems to lack elegance. I haven't decided whether that's simply due to classical training, or whether that's an actual fault of the above method.
Now, on to inheritance.
I have always understood inhertance in JavaScript to be difficult. Navigating the ins and outs of the prototype chain is not exactly clear. Until you use it with Object.create, which takes all the function-based, new-keyword redirection out of the equation.
Let's say we wanted to extend the above animal object, and make a human.
var human = Object.create(animal)
human.think = function()
{
console.log('Hmmmm...');
}
var myHuman = Object.create(human);
myHuman.legs = 2;
myHuman.favoriteWord = 'Hello';
This creates an object which has human as a prototype, which, in turn, has animal as a prototype. Easy enough. No misdirection, no "new object with a prototype equal to the prototype of the function". Just simple prototypal inheritance. It's simple, and straightforward. Polymorphism is easy, too.
human.speak = function()
{
console.log(this.favoriteWord + ', dudes');
}
Due to the way the prototype chain works, myHuman.speak will be found in human before it's found in animal, and thus our human is a surfer instead of just a boring old animal.
So, in conclusion (TLDR):
The pseudo-classical constructor functionality was kind of tacked on to JavaScript to make those programmers trained in classical OOP more comfortable. It is not, by any means, necessary, but it means giving up classical concepts such as member visibility and (tautologically) constructors.
What you get in return is flexibility, and simplicity. You can create "classes" on the fly - every object is, itself, a template for other objects. Setting values on child objects will not affect the prototype of those objects (i.e. if I used var child = Object.create(myHuman), and then set child.walk = 'not yet', animal.walk would be unaffected - really, test it).
The simplicity of inheritance is honestly mind-boggling. I've read a lot on inheritance in JavaScript, and written many lines of code attempting to understand it. But it really boils down to objects inherit from other objects. It's as simple as that, and all the new keyword does is muddle that up.
This flexibility is difficult to use to its fullest extent, and I'm sure I have yet to do it, but it's there, and it's interesting to navigate. I think most of the reason that it hasn't been used for a large project is that it simply isn't understood as well as it could be, and, IMHO, we're locked into the classical inheritance patterns we all learned when we were taught C++, Java, etc.
Edit
I think I've made a pretty good case against constructors. But my argument against factories is fuzzy.
After further contemplation, during which I've flip-flopped to both sides of the fence several times, I have come to the conclusion that factories are also unnecessary. If animal (above) were given another function initialize, it would be trivial to create and initialize a new object that inherits from animal.
var myDog = Object.create(animal);
myDog.initialize(4, 'Meow');
New object, initialized and ready for use.
#Raynos - You totally nerd sniped me on this one. I should be getting ready for 5 days of doing absolutely nothing productive.
As per your comment that the question is mainly "is constructor knowledge necessary?" I feel it is.
A toy example would be storing partial data. On a given data set in memory, when persisting I may only choose to store certain elements (either for the sake of efficiency or for data consistency purposes, e.g. the values are inherently useless once persisted). Let's take a session where I store the user name and the number of times they've clicked on the help button (for lack of a better example). When I persist this in my example, I do have no use for the number of clicks, since I keep it in memory now, and next time I load the data (next time the user logs in or connects or whatever) I will initialise the value from scratch (presumably to 0). This particular use case is a good candidate for constructor logic.
Aahh, but you could always just embed that in the static prototype: Object.create({name:'Bob', clicks:0}); Sure, in this case. But what if the value wasn't always 0 at first, but rather it was something that required computation. Uummmm, say, the users age in seconds (assuming we stored the name and the DOB). Again, an item that there is little use persisting, since it will need to be recalculated on retrieval anyway. So how do you store the user's age in the static prototype?
The obvious answer is constructor/initialiser logic.
There are many more scenarios, although I don't feel the idea is much related to js oop or any language in particular. The necessity for entity creation logic is inherent in the way I see computer systems model the world. Sometimes the items we store will be a simple retrieval and injection into a blueprint like prototype shell, and sometimes the values are dynamic, and will need to be initialised.
UPDATE
OK, I'm going to try for a more real-world example, and to avoid confusion assume that I have no database and need not persist any data. Let's say I'm making a solitaire server. Each new game will be (naturally) a new instance of the Game prototype. It is clear to me that their is initialiser logic required here (and lots of it):
I will, for example, need on each game instance not just a static/hard-coded deck of cards, but a randomly shuffled deck. If it were static the user would play the same game every time, which is clearly not good.
I may also have to start a timer to finish the game if the player runs out. Again, not something that can be static, since my game has a few requirements: the number of seconds is inversely related to the number of games the connected player has won so far (again, no saved info, just how many for this connection), and proportional to the difficulty of the shuffle (there is an algorithm that according to the shuffle results can determine the degree of difficulty of the game).
How do you do that with a static Object.create()?
Example of a staticly-clonable "Type":
var MyType = {
size: Sizes.large,
color: Colors.blue,
decay: function _decay() { size = Sizes.medium },
embiggen: function _embiggen() { size = Sizes.xlarge },
normal: function _normal() { size = Sizes.normal },
load: function _load( dbObject ) {
size = dbObject.size
color = dbObject.color
}
}
Now, we could clone this type elsewhere, yes? Sure, we would need to use var myType = Object.Create(MyType), but then we're done, yes? Now we can just myType.size and that is the size of the thing. Or we could read the color, or change it, etc. We haven't created a constructor or anything, right?
If you said there's no constructor there, you're wrong. Let me show you where the constructor is:
// The following var definition is the constructor
var MyType = {
size: Sizes.large,
color: Colors.blue,
decay: function _decay() { size = Sizes.medium },
embiggen: function _embiggen() { size = Sizes.xlarge },
normal: function _normal() { size = Sizes.normal },
load: function _load( dbObject ) {
size = dbObject.size
color = dbObject.color
}
}
Because we've already gone and created all the things we wanted and we've already defined everything. That's all a constructor does. So even if we only clone/use static things (which is what I see the above snippets as doing) we've still had a constructor. Just a static constructor. By defining a type, we have defined a constructor. The alternative is this model of object construction:
var MyType = {}
MyType.size = Sizes.large
But eventually you're going to want to use Object.Create(MyType) and when you do, you will have used a static object to create the target object. And then it becomes the same as the previous example.
The short answer to your question "Do we need factories/constructors in prototypical OO?" is no. Factories/Constructors serve 1 purpose only: initialize the newly created object (an instance) to a specific state.
That being said, it is often uses because some objects need initialization code of some sort.
Let's use the component-based entity code you provided. A typical entity is simply a collection of components and few properties:
var BaseEntity = Object.create({},
{
/* Collection of all the Entity's components */
components:
{
value: {}
}
/* Unique identifier for the entity instance */
, id:
{
value: new Date().getTime()
, configurable: false
, enumerable: true
, writable: false
}
/* Use for debugging */
, createdTime:
{
value: new Date()
, configurable: false
, enumerable: true
, writable: false
}
, removeComponent:
{
value: function() { /* code left out for brevity */ }
, enumerable: true
, writable: false
}
, addComponent:
{
value: function() { /* code left out for brevity */ }
, enumerable: true
, writable: false
}
});
Now the following code will create new entities based on the 'BaseEntity'
function CreateEntity()
{
var obj = Object.create(BaseEntity);
//Output the resulting object's information for debugging
console.log("[" + obj.id + "] " + obj.createdTime + "\n");
return obj;
}
Seems straight forward enough, until you go to reference the properties:
setTimeout(CreateEntity, 1000);
setTimeout(CreateEntity, 2000);
setTimeout(CreateEntity, 3000);
outputs:
[1309449384033] Thu Jun 30 2011 11:56:24 GMT-0400 (EDT)
[1309449384033] Thu Jun 30 2011 11:56:24 GMT-0400 (EDT)
[1309449384033] Thu Jun 30 2011 11:56:24 GMT-0400 (EDT)
So why is this? The answer is simple: because of prototype based inheritance. When we created the objects, there wasn't any code to set the properties id and createdTime on the actual instance, as is normally done in constructors/factories. As a result, when the property is accessed, it pulls from the prototype chain, which ends up being a single value for all entities.
The argument to this is that the Object.create() should be passed the second parameter to set this values. My response would simply be: Isn't that basically the same as calling a constructor or using a factory? It's just another way of setting an object's state.
Now with your implementation where you treat (and rightfully so) all prototypes as a collection of static methods and properties, you do initialize the object by assigning the values of the properties to the data from a data source. It may not be using new or some type of factory, but it is initialization code.
To summarize:
In JavaScript prototype OOP
- new is not needed
- Factories are not needed
- Initialization code is usually needed, which is normally done through new, factories, or some other implementation that you don't want to admit is initializing an object
I want to write some Javascript classes which extend DOM nodes (so that I can then insert instances of my class directly into the DOM), but am having difficulty finding out which class/prototype I should inherit from.
E.g.:
function myExtendedElement() {
this.superclass = ClassA;
this.superclass();
delete this.superclass;
}
But what should ClassA be?
It's not a good idea to do this.
First of all, to inherit from DOM element, you need to have access to that element's prototype. The problem is that not all browsers provide access to prototypes of DOM elements. Newer Gecko and WebKit -based clients, for example, expose some of these prototypes as global objects - HTMLDivElement, HTMLElement, Element, Node, etc.
For example, plain DIV element usually has a prototype chain similar to:
HTMLDivElement.prototype -> HTMLElement.prototype -> Element.prototype
-> Node.prototype -> Object.prototype -> null
You can access any of them and extend or inherit from as desired. But again, even though you can, I strongly advise not to.
When browser doesn't expose these prototypes, you're pretty much out of luck. You can try retrieving them by following constructor property of DOM element itself -
document.createElement('div').constructor;
- but then there's no guarantee that element has constructor property (e.g. IE6 doesn't) and even if it does, that this property references "correct" object. If, after all, constructor does reference correct object, there's still no guarantee that this objects is allowed to be augmented at all. The truth is that host objects are allowed to implement completely bizarre behavior and do not even have to follow rules that native JS objects follow (you can find dozens of such examples in real life).
Second reason you want to avoid inheriting from DOM element prototypes is that mechanism of such inheritance is not really specified anywhere; it could be quirky, unpredictable and overall fragile and unreliable.
Yes, you can create a constructor that would initialize objects with proper prototype chain (i.e. having DOM prototype in it):
function MyDivElement(){}
MyDivElement.prototype = HTMLDivElement.prototype;
var myDiv = new MyDivElement();
typeof myDiv.appendChild; // "function"
- but this is as much as it goes, and usefulness of this whole approach becomes limited by having certain methods in prototype and nothing else -
typeof myDivElement.nodeName; // "undefined"
myDivElement.innerHTML = '<span>foo<\/span>';
myDivElement.childNodes; // Error
Until some standard specifies exact mechanism for inheriting from DOM prototypes (and browsers actually implement that mechanism), it's best to leave them alone, and perhaps try alternative approach - e.g. wrapper or decorator patterns rather than prototype one :)
Old Q but there's a better answer than "Do" or "Don't" now that IE6 is mostly defunct. First of all prototyping core ECMA endpoint-inheritance constructors like 'Array' is pretty harmless and useful if you do it properly and test to avoid breaking existing methods. Definitely stay away from Object and think real hard before messing with Function, however.
If you're sharing code between a lot of people/authors, or dealing with DOM uncertainty, however, it's typically better to create adapter/wrapper objects with a new factory method to use in an inheritance-scheme.
In this case I wrote document.createExtEl to create wrapped DOM elements whose accessible properties are all available via prototype.
Using the following, your "superclass" for divs would be HTMLExtDivElement (in this case globally available - ew, but it's just an example). All references to the original HTMLElement instance's available properties live inside the wrapper's prototype. Note: some old IE properties can't be passed as references or even accessed without throwing errors (awesome), which is what the try/catch is for.
You could normalize common properties by adding logic to put missing or standardized properties in right after the loop wraps instance-available properties but I'll leave that to you.
Now for the love of Pete, don't ever use my code to write some cascading 16-times inheritance foolishness and then implement in some ironically popular library we're all forced to deal with or I will hunt you down and loudly quote "Design Patterns" at you while throwing rotten fruit.
//Implementation just like document.createElement()
//document.createExtEl('div').tagName === 'DIV'
document.createExtEl = ( function(){ //returns a function below
var htmlTags = ['div','a'], //... add all the element tags you care to make extendable here
constructorMap = {},
i = htmlTags.length;
while(i--){
thisTag = htmlTags[i].toLowerCase();
constructorMap[ thisTag ] = function(){
var elObj = document.createElement(thisTag),
thisProto = this.constructor.prototype,
constructorName = 'HTMLExt' + thisTag.charAt(0).toUpperCase() + thisTag.slice(1) + 'Element';
alert(constructorName);
window[constructorName] = this.constructor; //adds a global reference you can access the new constructor from.
for(var x in elObj){ try{ thisProto[x] = elObj[x]; } catch(e){} }
}
}
//all of the above executes once and returned function accesses via closure
return function(tagName){
return new constructorMap[tagName.toLowerCase()]();
}
} )()
//Now in the case of a superclass/constructor for div, you could use HTMLExtDivElement globally
In 2020, you can easily create a custom element by extending HTML elements.
class AppDrawer extends HTMLElement {...}
window.customElements.define('app-drawer', AppDrawer);
// Or use an anonymous class if you don't want a named constructor in current scope.
window.customElements.define('app-drawer', class extends HTMLElement {...});
More info and reference: Custom Elements
You can simply add new functions to the DOM prototypes, eg.
Element.prototype.myNameSpaceSomeFunction = function(...){...}
Then myNameSpaceSomeFunction will exist on all elements.
I've found a hack that works... at least, it enables me to access the extended object properties via the DOM element and vice versa. But it's hardly elegant.
var DOMelement = document.getElementById('myID'); // or $('#myID')[0]; in jQuery
DOMelement.extended = new extensionClass(this);
function extensionClass(element) {
this.element = element;
...
}