I receive a bunch of objects via JSON which ultimately need to have some instance member functions.
Is there a way to do this without copying the data?
For example:
var DataObject = function() {};
DataObject.prototype.add = function() { return this.a + this.b; };
var obj = JSON.parse('{"a":1, "b":2}');
// Do something to obj to make it inherit from DataObject
console.assert( obj.add() === 3 );
I've tried setting obj.prototype = DataObject.prototype but that doesn't seem to work. What am I missing?
Well, in ECMAScript6 (in IE11, and every other non ie browser today), that would be __proto__
obj.__proto__ = Object.create(DataObject.prototype);
[fiddle]
Generally, make sure you only do this at the object creation case, otherwise it can be very risky to do.
Also note, setting the protoype explicitly is not always faster than copying two properties, as you can see here so you have to be sure there is actual gain here.
Related
I wonder, how to make such a thing in JavaScript or is such a stuff even possible in it?
For example, I have:
// ps: this code is abstract and just an idea, not real one
var A = function( inputInstance ) {
if ( inputInstance !== undefined )
this = inputInstance;
else
this = new B();
};
I'm interested in this possible or impossible stuff in JavaScript, because similar thing is possible in various functional programming languages, e.g. F#:
http://msdn.microsoft.com/en-us/library/dd233237.aspx
// This object expression specifies a System.Object but overrides the
// ToString method.
let obj1 = { new System.Object() with member x.ToString() = "F#" }
printfn "%A" obj1
It can be very useful, if such thing could be possible in JavaScript, but I suggest it isn't possible, because I don't want to set several properties from one instance just for a object copy:
this.id = inputObject.id;
this.guid = inputObject.guid;
this.data = inputObject.data;
...
I don't want to declare each member of inputObject, I want to set/copy all existed properties from one instance to another just by one elegant line like in functional languages like F#, so is it possible in JavaScript?
No, it is not possible. You can, however, use a loop.
for (var key in inputInstance) {
// Remove this if you want to copy inherited properties too
if (inputInstance.hasOwnProperty(key)) {
this[key] = inputInstance[key];
}
}
I'm working on an AngularJS SPA and I'm using prototypes in order to add behavior to objects that are incoming through AJAX as JSON. Let's say I just got a timetable x from an AJAX call.
I've defined Timetable.prototype.SomeMethod = function() and I use https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf in order to set the prototype of x to TimeTable.prototype. I have the polyfill in place too.
If I call x.SomeMethod() this works in IE > 9, FF, Chrome etc. However, IE 9 gives me a headache and says throws an error stating 'x does not have property or member SomeMethod'.
Debugging in IE shows me that the _proto_ of x has SomeMethod() in the list of functions, however, calling x.SomeMethod() gives the same error as described.
How can I make this work in IE9 ?
More comment than answer
The main problem with "extending" a random object retrieved from some other environment is that javascript doesn't really allow random property names, e.g. the random object may have a property name that shadows an inherited property. You might consider the following.
Use the random object purely as data and pass it to methods that access the data and do what you want, e.g.
function getName(obj) {
return obj.name;
}
So when calling methods you pass the object to a function that acts on the object and you are free to add and modify properties directly on the object.
Another is to create an instance with the methods you want and copy the object's properties to it, but then you still have the issue of not allowing random property names. But that can be mitigated by using names for inherited properties that are unlikely to clash, e.g. prefixed with _ or __ (which is a bit ugly), or use a naming convention like getSomething, setSomething, calcLength and so on.
So if obj represents data for a person, you might do:
// Setup
function Person(obj){
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
this[p] = obj[p];
}
}
}
Person.prototype.getName = function(){
return this.name;
};
// Object generated from JSON
var dataFred = {name:'fred'};
// Create a new Person based on data
var p = new Person(dataFred);
You might even use the data object to create instances from various consructors, e.g. a data object might represent multiple people, or a person and their address, which might create two related objects.
This is how I solved it at the end:
Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
if (!isIE9()) {
obj.__proto__ = proto;
} else {
/** IE9 fix - copy object methods from the protype to the new object **/
for (var prop in proto) {
obj[prop] = proto[prop];
}
}
return obj;
};
var isIE9 = function() {
return navigator.appVersion.indexOf("MSIE 9") > 0;
};
I'm making a class that will be recreated many times, and in order to save memory I need to thoroughly delete it. Basically I need to access its containing variable if possible.
Here's the example:
function example(){
this.id=0;
this.action=function(){alert('tost');}
this.close=function(){ delete this;}
}
var foo=new example();
My question is:
How can I get access to the foo variable from within the example function so I can remove it?
window.foo will access that global variable.
this.close=function(){ delete window.foo; }
However, I remember there is something fishy with global variables, delete and window, so you might want to do otherwise, and simply use window.foo = null; for example.
If you want to access a variable defined in another function, you'll want to read the answers to this SO question.
Since what you want is to allow the garbage collector to release that object, you need to ensure that there are no references left to the object. This can be quite tricky (i.e. impossible) because the code manipulating the object can make multiple references to it, through global and local variables, and attributes.
You could prevent direct reference to the object by creating a proxy to access it, unfortunately javascript doesn't support dynamic getters and setters (also called catch-alls) very well (on some browseres you might achieve it though, see this SO question), so you can't easily redirect all field and method (which are just fields anyway) accesses to the underlying object, especially if the underlying object has many fields added to it and removed from it dynamically (i.e. this.anewfield = anewvalue).
Here is a smiple proxy (code on jsfiddle.net):
function heavyobject(destroyself, param1, param2) {
this.id=0;
this.action=function(){alert('tost ' + param1 + "," + param2);};
this.close=function(){ destroyself(); }
}
function proxy(param1, param2) {
object = null;
// overwrites object, the only reference to
// the heavyobject, with a null value.
destroyer = function() { object = null; };
object = new heavyobject(destroyer, param1, param2);
return function(fieldname, setvalue) {
if (object != null) {
if (arguments.length == 1)
return object[fieldname];
else
object[fieldname] = setvalue;
}
};
}
var foo = proxy('a', 'b');
alert(foo("action")); // get field action
foo("afield", "avalue"); // set field afield to value avalue.
foo("action")(); // call field action
foo("close")(); // call field close
alert(foo("action")); // get field action (should be 'undefined').
It works by returning a function that when called with a single argument, gets a field on the wrapped object, and when called with two arguments sets a field. It works by making sure that the only reference to the heavyobject is the object local variable in the proxy function.
The code in heavyobject must never leak this (never return it, never return a function holding a reference to var that = this, never store it into a field of another variable), otherwise some external references may be created that would point to the heavyobject, preventing its deletion.
If heavyobject's constructor calls destroyself() from within the constructor (or from a function called by the constructor), it won't have any effect.
Another simpler proxy, that will give you an empty object on which you can add fields, read fields, and call methods. I'm pretty sure that with this one, no external reference can escape.
Code (also on jsfiddle.net):
function uniquelyReferencedObject() {
object = {};
f = function(field, value) {
if (object != null) {
if (arguments.length == 0)
object = null;
else if (arguments.length == 1)
return object[field];
else
object[field] = value;
}
};
f.destroy = function() { f(); }
f.getField = function(field) { return f(field); }
f.setField = function(field, value) { f(field, value); }
return f;
}
// Using function calls
o = uniquelyReferencedObject();
o("afield", "avalue");
alert(o("afield")); // "avalue"
o(); // destroy
alert(o("afield")); // undefined
// Using destroy, getField, setField
other = uniquelyReferencedObject();
other.setField("afield", "avalue");
alert(other.getField("afield")); // "avalue"
other.destroy();
alert(other.getField("afield")); // undefined
The truth is that you can not delete objects in Javascript.
Then you use delete operator, it accepts the property of some object only.
So, when you use delete, in general you must pass to it something like obj.p. Then you pass just a variable name actually this means 'property of global object', and delete p is the same as delete window.p. Not sure what happens internally on delete this but as a result browser just skip it.
Now, what we actually deleting with delete? We deleting a reference to object. It means object itself is still somethere in memory. To eliminate it, you must delete all references to concrete object. Everythere - from other objects, from closures, from event handlers, linked data, all of them. But object itself doest have information about all this references to it, so there is no way to delete object from object itself.
Look at this code:
var obj = <our object>;
var someAnother = {
...
myObjRef: obj
...
}
var someAnotherAnother = {
...
secondRef : obj
...
}
To eliminate obj from memory you must delete someAnother.myObjRef and someAnoterAnother.secondRef. You can do it only from the part of programm which knows about all of them.
And how we delete something at all if we can have any number of references everythere? There are some ways to solve this problem:
Make only one point in program from there this object will be referenced. In fact - there will be only one reference in our program. and Then we delete it - object will be killed by garbage collector. This is the 'proxy' way described above. This has its disadvantages (no support from language itself yet, and necessarity to change cool and nice obj.x=1 to obj.val('x',1). Also, and this is less obvious, in fact you change all references to obj to references to proxy. And proxy will always remain in memory instead of object. Depending on object size, number of objects and implementation this can give you some profit or not. Or even make things worse. For example if size of your object is near size of proxy itself - you will get no worth.
add to every place there you use an object a code which will delete reference to this object. It is more clear and simple to use, because if you call a obj.close() at some place - you already knows everything what you need to delete it. Just instead of obj.close() kill the refernce to it. In general - change this reference to something another:
var x = new obj; //now our object is created and referenced
x = null;// now our object **obj** still im memory
//but doest have a references to it
//and after some milliseconds obj is killed by GC...
//also you can do delete for properties
delete x.y; //where x an object and x.y = obj
but with this approach you must remember that references can be in very hard to understand places. For example:
function func() {
var x= new obj;// our heavy object
...
return function result() {
...some cool stuff..
}
}
the reference is stored in closure for result function and obj will remain in memory while you have a reference to result somethere.
It hard to imagine object that is heavy itself, most realistic scenario - what you have some data inside it. In this case you can add a cleanup function to object which will cleans this data. Let say you have an gigant buffer (array of numbers for example) as a property of the object, and if you want to free memory - you can just clear this buffer still having object in memory as a couple dozens of bytes. And remember to put your functions to prototype to keep instances small.
Here is a link to some very detailed information on the JavaScript delete operator.
http://perfectionkills.com/understanding-delete/
I'm currently trying to test whether it's possible to make one object inherit from another object AFTER both objects have been created using literals. I tried to aim the constructors and prototypes at one another but it seems like no matter what, the only way Im going to pull this off is by building a new object using one of the pre-existing ones.. Let me know if I'm wrong. Here was my quick attempt to solve the problem.
Object.relate = function(parent, child){
function F(){};
F.prototype = parent;
child.constructor = F;
}
alpha = {a:1};
beta = {b:2};
Object.relate(alpha, beta);
In your code beta can access properties defined on alpha by using beta.constructor.prototype.
When you use new on a function like
var obj = new F();
then obj.__proto__ is automatically set to F.prototype. When you have already existing objects and you are not creating the object using new you must set the __proto__ by yourself.
obj.__proto__ = F.prototype;
So in your example you need to call
child.__proto__ = parent;
Resources:
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/proto
http://killdream.github.com//blog/2011/10/understanding-javascript-oop/index.html#sec-4-1
The problem is that now every browser expose the __proto__ property. IE doesn't support it so you cannot create a browser-compatible solution without actually creating a new object and copying the properties.
This method seems to work for me (tested in Chrome):
function relate(parent, child){
child.__proto__ = parent;
}
alpha = {a:1};
beta = {b:2};
relate(alpha, beta);
console.log(beta.a);
Output: '1'.
var obj = {
destroy: function(){this = null;}
};
obj.destroy();
This works in Chrome, however firefox is throwing an error referencing this for some reason. Is there a better way to kill this object within a method?
Error:
invalid assignment left-hand side
[Break On This Error] destroy: function(){this = null;}
Not sure why Chrome allows for it but you can't assign a value to this. You can reference this, but you can't assign a value to it.
If you have some array destruction you want to perform you can reference this.myArrayName within your destroy method and free up whatever you're trying to release, but you can't just assign null to this to destroy an instance.
I suppose you could try something like this:
var foo = {
// will nullify all properties/methods of foo on dispose
dispose: function () { for (var key in this) this[key] = null; }
}
foo.dispose();
Pretty much as close as you can get to legally nullifying "this"...
Happy coding.
B
Call me old fashion, but:
foo = null;
I'm not sure why you're making this difficult. Javascript is a garbage collected language. All you have to do to allow something to be freed is to make sure there are no more references to it anywhere.
So, if you start with:
var obj = {
data: "foo";
};
and now you want to get rid or "free" that object, all you have to do is clear the reference to it with:
obj = null;
Since there are no longer any references in your code to that data structure that you originally defined and assigned to obj, the garbage collector will free it.
An object cannot destroy itself (because other things may have references to it). You allow it to be freed by removing all references to it. An object can clear out it's own references to other things, though that is generally not required as removing all references to the object itself will also take care of the references it holds (with the exception of some bugs with circular references between JS and the DOM in certain older browsers - particular IE).
One time when you might explicitly "delete" something is if you have a property on an object that you wish to remove. So, if you have:
var obj = {
data: "foo",
count: 4
};
And you wish to remove the "data" property, you can do that with this:
delete obj.data;
of if the property/key was assigned programmatically via a variable like this:
var key = "xxx";
obj[key] = "foo";
you can remove that key with:
delete obj[key];