Since Web-Worker JSON serialize data between threads, something like this doesn't work:
worker.js
function Animal() {}
Animal.prototype.foobar = function() {}
self.onmessage = function(e) {
self.postMessage({animal: new Animal()})
}
main.js
let worker = new Worker('worker.js')
worker.onmessage = function(e) {
console.log(e.data)
}
worker.postMessage('go!')
The outcome would be a simple object with the loss of the foobar prototype method.
Is it possible to transfer the custom object back to the main thread without losing its prototype methods? Like, would this be possible with ArrayBuffer? I'm not familiar with that stuff, so I'm a bit lost.
Assuming you program both the client and the webservice you can define the Animal function in boths sides
Then you can add to Animal.prototype (in both sides) toJson method to pass the info you need to recreate the object (and may be choose some attribute to define the className)
You define a reviver that use the reverse process
Then when you post you must always JSON.stringify(e)
In the onmessage you JSON.parse(m,reviver)
function Animal(name, age){
var private_name = name;
this.public_age = age;
this.log = function(){
console.log('Animal', private_name, this.public_age);
}
this.toJson = function(){
return JSON.stringify({
__type__:'Animal', // name of class
__args__:[this.public_age, private_name] // same args that construct
});
}
}
Animal.prototype.age = function(){
return this.public_age;
}
var a = new Animal('boby', 6);
worker.postMessage(JSON.stringify(a));
function reviver(o){
if(o.__type__){
var constructor=reviver.register[o.__type__];
if(!constructor) throw Error('__type__ not recognized');
var newObject = {};
return constructor.apply(newObject, o.__args__);
}
return o;
}
reviver.register={}; // you can register any classes
reviver.register['Animal'] = Animal;
worker.onmessage = function(m){
var a = JSON.parse(e, reviver);
}
There is a simple way without set prototypes, also without convert to string with JSON.stringify, you need to build to function:
toObject(instance):obj, instance is and instance class and will be converted to and object
toInstanceClass(obj):instance, obj is and object and will return and instance from your class
You need to pass your obj to the worker, in the worker you'll build your instance from your class, make all your operations and return like and obj
In your main thread you'll need to rebuild your instance from your class by the returned obj from worker, that's all.
Related
I have a Javascript library that I want to use on a web browser and also on a Node.js backend. In the library, I have multiple objects with methods defined like so:
function foo() {
this.bar = 200;
this.someMethod = function(baz) {
return this.bar + baz;
};
}
var x = new foo();
And I can use it in the client or the Node.js server by doing the following:
x.someMethod(5);
=> (returns 205)
Now, when I JSON.stringify my object, it shows up without the method.
var string = JSON.stringify(x);
=> {"bar":200}
Which means I can't unpack the JSON on my server and use the same methods.
var parsed = JSON.parse(string);
document.write(parsed.someMethod(5));
=> (doesn't do anything. the methods are gone!)
In a class based system I'd just use a copy constructor. Something that would reconstruct the object from JSON.
function foo_copy_constructor(parsed_json) {
f = new foo();
f.bar = parsed_json.bar;
return f;
}
var z = foo_copy_constructor(parsed);
z.someMethod(5);
=> (returns 205 like it should)
( jsfiddle: http://jsfiddle.net/7FdDe/ )
Basically, Is there a better way than this?
Many of my objects contain instances of other objects I've written with their own methods, and this seems like it would get tedious to build a constructor for every object since both the client and the server use the same library and object definitions. I know that JavaScript is based on prototypes, but I don't really understand them since I've just started with JavaScript and am used to Python and class-based languages.
Thanks for any and all help!
JSON.stringify only stringifies the objects that have the toJSON method. So you could simply add the toJSON method to your methods. (Remember, functions are objects too.)
function A() {
this.method = function() { console.log(1); };
}
var c = new A();
JSON.stringify(c);
"{}"
A.prototype.otherMethod = function() { console.log(1); };
var c = new A();
JSON.stringify(c);
"{}"
Function.prototype.toJSON = function() { return this.toString(); };
JSON.stringify(c);
"{"method":"function () { console.log(1); }"}"
However, when parsing this back, you get the function as a string. So you have to the strings back to functions with something like this:
var d = JSON.parse(JSON.stringify(c));
Object.keys(d).forEach(function(k) {
// If it starts with "function"
if (/^function/.test(d[k])) {
// Get the body of the function
var f = d[k].match(/{(.*)}/).pop();
// Replace the string with a real function
d[k] = new Function(f);
}
});
d.method();
1
However, instead of messing with javascript like this, I'd rather suggest that you use a well-tested library like now.js.
I find this is most recommended way to do inheritance in javascript.
function extend(Child, Parent) {
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
}
what if I already have methods in child's prototype, aren't they will overwrite, shouldn't we preserve them.
function extend(Child, Parent) {
var c = child.prototype;
var oldProto = new C();
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
for(var i in oldProto ){
Child.prototype[i] = oldProto[i]
}
}
I'm not sure if this is any good to you, but it's well important to remember: prototypes are not the same things as classes. What you're doing is trying to make JS behave like a traditional OO language, which is trying to teach a dolphin to dance ballet, or forcing a tiger to become vegan: Admirable, but destined to end in tears.
I can't really see why you'd want to use the extend function to do whatever it is you're trying to do. Why not simply use this:
function Parent()
{};
function Child()
{};
//augment parent proto
Parent.prototype.parentMethod1 = function()
{};
//set Child's proto to Parent
Child.prototype = new Parent();
Child.prototype.constructor = Child;
//Then augment the Child's prototype
Child.prototype.childMethod1 = function()
{};
var foo = new Child();
foo.parentMethod1();//works
foo.childMethod1();//works, too
IMO, this solves the problem entirely. Sure, it's a tad more verbose, but OOP always is.
The pattern you're trying to achieve is called multiple inheritance. And it's highly not recommended for the use because of the issue you're experiencing, called diamond problem. Just use mixin pattern instead.
The code below is the one of the best I have seen for doing inheritance in JavaScript.
Object.create(proto [, propertiesObject ]) is discussed on MDN here.
Below, Jon defines a base empty object called ExtendBase then adds a function property called extend which is not enumerable which takes as its argument a single new object.
That object should contain enumerable properties such as methods and data that will be added to the base object.
He gets all the enumerable properties from the passed object, then creates an array of the necessary descriptors to pass into Object.create using those properties' names. He then uses the parent object as the prototype and resultant descriptors as new properties to be added to the child object directly in the Object.create() call.
As you can see, you can use an object argument with properties, including methods, to extend a parent without losing that passed object's properties with the result being a child object with the parent as the prototype and the enumerable objects of the passed object added directly to the child.
However, this maintains a clean prototype chain while intending to extend parent objects using other objects which are created sanely to extend the parent into a new child in a way that makes sense:
Live sample here (Press F12 in Chrome for console output, or use FireBug in FireFox, etc.)
JavaScript:
// Original Author: FireFly - Jonas Höglund - ##javascript channel
// on irc.freenode.net - see THANKS File. Updated to private data
// members and passable initial parameters by Scott Sanbar
///////////////
// Library code
///////////////
var ExtendBase = {};
Object.defineProperty(ExtendBase, 'extend', {
enumerable:false, value:function (obj) {
'use strict';
var descs = {};
Object.getOwnPropertyNames(obj).forEach(function (key) {
descs[key] = Object.getOwnPropertyDescriptor(obj, key)
});
return Object.create(this, descs);
}
});
///////////////
// Sample Usage
///////////////
function PersonObj(nam) {
return {
name:new function () {
var name = nam;
this.set = function (value) {
name = value;
};
this.get = function () {
return name;
}
},
// A person can tell you its name.
talk:function () {
return "Hello, I'm " + this.name.get();
}
}
}
;
function WorkingPersonObj(occ) {
return {
occupation:new function () {
var occupation = occ;
this.set = function (value) {
occupation = value;
};
this.get = function () {
return occupation;
}
},
// A working person also tells you their occupation when they talk.
talk:function () {
return Person.talk.call(this) + " and I am a " + this.occupation.get();
}
}
}
;
var hush = {
hush:function () {
return "I am supposed to be quiet";
}
};
var Person = ExtendBase.extend(new PersonObj('Harry'));
var WorkingPerson = Person.extend(new WorkingPersonObj('wizard'));
var wp1 = WorkingPerson.extend(hush);
console.log(wp1.talk()); // "Hello, I'm Harry and I am a wizard"
console.log(wp1.hush()); // "I am supposed to be quiet"
wp1.name.set("Elijah");
wp1.occupation.set("prophet");
console.log(wp1.talk()); // "Hello, I'm Elijah and I am a prophet"
console.log(wp1.name.get());
console.log(wp1.occupation.get());
Considering object creation patterns with private properties, one way to do is :
function MyStack (){
var list = [],
index = 0;
this.push = function(val){
return list[index++] = val;
};
this.pop = function(){// ...}
}
var stack1 = new MyStack(); stack1.push(5);
var stack2 = new MyStack(); stack2.push(11);
Problem with this: Every instance of Stack has it's own copy of methods 'push' and 'pop'.
Another way for implementing constructor method is:
function MyStack(){
this.list = [];
this.index = 0;
}
MyStack.prototype = {
insert: function(val){
return this.list[this.index++] = val;
},
pop:function(){//...}
}
Problem here: We lose the privacy of list and index.
Is there a way, such that we can have both methods reuse among instances and privacy of properties ?
I understand that we can have this for methods that don't operate on any state of the object, but I am talking more about those methods that do operate on the state.
Yes. I've edited this code so it's actually fully functional as you had intended it to work. It seems a bit redundant to me, but, it does provide you the ability to provide a public interface, but to keep your variables private and control the way the user interacts with them.
function MyStack(){
var list = [];
var index = 0;
this.getIndex = function(){
return index;
}
this.setIndex = function(val){
index = val;
}
this.list = function(val){
if(val){
// setter if a value was provided. Illustrating how you can control
// index, which I assume is the point of having these things private
// to begin with
return list[this.setIndex(this.getIndex() + 1)] = val;
}
// always return list - acts like a getter
return list;
}
}
MyStack.prototype = {
insert: function(val){
return this.list(val);
},
pop:function(){}
}
var stack1 = new MyStack();
stack1.insert(5);
var stack2 = new MyStack();
stack2.insert(11);
You should check out John Resig's Simple Javascript Inheritance. It is a great read, and it has been extended to provide support for privates, aptly called Privates.js;
A constructor function may return any object (not necesserily this). One could create a constructor function, that returns a proxy object, that contains proxy methods to the "real" methods of the "real" instance object. This may sound complicated, but it is not; here is a code snippet:
var MyClass = function() {
var instanceObj = this;
var proxyObj = {
myPublicMethod: function() {
return instanceObj.myPublicMethod.apply(instanceObj, arguments);
}
}
return proxyObj;
};
MyClass.prototype = {
_myPrivateMethod: function() {
...
},
myPublicMethod: function() {
...
}
};
The nice thing is that the proxy creation can be automated, if we define a convention for naming the protected methods. I created a little library that does exactly this: http://idya.github.com/oolib/
I think in both approaches you mentioned, When ever object is created using constructor pattern the properties will get copied to its objects. This you mentioned for the 1st approach as the concern. I feel the same will be applied for the second approach also along with your concern in this approach.
We generally go to the second approach you mentioned when ever we want to extend the properties of "MyStack" to some other class.
Lets say i want to extend your class MyStack to MyTest like below
var dummy = function();
dummy.prototype = MyStack.prototype;
var MyTest = function(){
};
MyTest.prototype = new dummy(); // Assigning MyStack properties to MyTest
var obj = new MyTest();
How can I add data/functions to all instances of a javascript object created by a constructor so that all instances have the same reference and not a copy of it?
Basically implementing the equivalent of a static method in C#.
For example, given the following code which creates a Widget class.
(function() {
var Widget = function() {
};
Widget.prototype.init = function(data) {
this.data = data;
};
this.Widget = Widget;
}).call(this);
var instance1 = new Widget();
instance1.init('inst1');
var instance2 = new Widget();
instance2.init('inst2');
alert(instance1.data); // inst1
alert(instance2.data); // inst2
In the above case each instance has it's own copy of the data property. However I want to add a function that sets data for all current and future instances.
My current solution is to add a function to the constructor function object, not to it's prototype. See below for example. Is there any pitfalls to this and is there a better way?
(function() {
var Widget = function() {
};
Widget.prototype.init = function(data) {
this.data = data;
};
Widget.addStaticData = function(data) {
this.staticData = data;
};
Widget.prototype.getStaticData = function() {
return Widget.staticData;
};
this.Widget = Widget;
}).call(this);
var instance1 = new Widget();
instance1.init('inst1');
Widget.addStaticData('static');
var instance2 = new Widget();
instance2.init('inst2');
alert(instance1.data); // inst1
alert(instance2.data); // inst2
alert(instance1.getStaticData()); // static
alert(instance2.getStaticData()); // static
Three pitfalls that I can think of:
methodological: the prototype is the place for shared, reused, inherited functionality/properties - utilise it as such
performance: it is quicker to inherit than to set each time on an instance. John Resig (jQuery creator) did some benchmarking on this in a blog post that I appear unable to find at present.
losing the split between inherited and own properties. If you apply everything to an instance via the constructor, everything is an instance property.
Everything via constructor:
function Dog() { this.legs = 4; }
var fido = new Dog();
fido.name = 'Fido';
for (var i in fido) if (fido.hasOwnProperty(i)) alert(i+' = '+fido[i]);
...alerts both properties as they are deemed the instance's own.
Via prototype and constructor
function Dog2() { }
Dog2.prototype.legs = 4;
var fido = new Dog2();
fido.name = 'Fido';
for (var i in fido) if (fido.hasOwnProperty(i)) alert(i+' = '+fido[i]);
...alerts just name because that is the only instance property. (Nonetheless, fido.legs is retrievable - but it comes from the prototype).
[EDIT - in response to the OP's commet below]
If you want a static method, then that should be added to the function after its declaration.
function Dog() {}
Dog.static = function() {}
Consider a local variable staticData instead of the Widget.staticData property. That way, an external command won't be able to write the data directly, so the only way to write it will be through the addStaticData function:
(function () {
var Widget = function () {};
var staticData;
Widget.addStaticData = function ( obj ) {
staticData = obj.data;
};
Widget.prototype.init = function () {
var data = staticData;
// use data
// or just use the staticData variable directly
};
this.Widget = Widget;
}).call( this );
With your code, one could just execute this:
Widget.staticData = { data: 'COMPROMISED!' };
to change the static data. Since you have a dedicated function for setting the static data, you probably don't want it to be possible to change the static data in other ways.
With my code, the above statement has no effect, and the static data can only be changed via the addStaticData function.
I have been at this for hours and just can't get it quite right. I have an object with methods that works fine. I need to save it as a string using JSON.stringify and then bring it back as an object and still use the same methods.
function Workflow(){
this.setTitle = function(newtitle){this.title = newtitle; return this;};
this.getTitle = function(){return this.title;};
}
function testIn(){
var workflow = new Workflow().setTitle('Workflow Test');
Logger.log(workflow);//{title=Workflow Test}
Logger.log(workflow.getTitle()); //Workflow Test
var stringy = JSON.stringify(workflow);
var newWorkflow = Utilities.jsonParse(stringy);
Logger.log(newWorkflow); //{title=Workflow Test}
//Looks like the same properties as above
Logger.log(newWorkflow.getTitle());//Error can't find getTitle
}
I think I should prototype the new object but nothing seems to work.
Please help I have very little hair left.
You need to copy the method to the new object:
newWorkflow.getTitle = workflow.getTitle;
you are losing your functions when you stringify and parse.
if you have access to jquery, the $.extend is handy (if not, just copy&paste form jquery source)
here's a demo:
http://jsfiddle.net/VPfLc/
Serializing to JSON won't store executable code. It's being removed from your object when calling JSON.stringify. Your best bet is to make the object so it can be initialized when created.
function Workflow(){
this.initialize = function(properties) { this.title = properties.title; }
this.setTitle = function(newtitle){this.title = newtitle; return this;};
this.getTitle = function(){return this.title;};
}
function testIn(){
var workflow = new Workflow().setTitle('Workflow Test');
Logger.log(workflow);//{title=Workflow Test}
Logger.log(workflow.getTitle()); //Workflow Test
var stringy = JSON.stringify(workflow);
var newWorkflow = new Workflow().initialize(Utilities.jsonParse(stringy));
Logger.log(newWorkflow); //{title=Workflow Test}
//Looks like the same properties as above
Logger.log(newWorkflow.getTitle());//Error can't find getTitle
}
All you have to do is use call.
Workflow.call(newWorkflow);
EDIT:
If your actual Workflow() implementation sets any attributes to default values during its initilization then calling on your new json object will also reset those. Which is what I'm assuming is going on, without being able to look at your actual implementation code.
If that is the case, then you have two options.
1) Rather than blindly initilize your object (and assuming its empty), conditionally initilize your variables.
function Workflow(){
this.setTitle = function(newtitle){this.title = newtitle; return this;};
this.getTitle = function(){return this.title;};
this.array = this.array || [];
}
for new empty objects. this.array will be null, and it'll be set to a new array. calling Workflow on a existing object that already has that property, it'll leave it alone.
2) Extract your methods into an Extension Module
function Workflow(){
this.array = this.array || [];
// Other work
// Lastly include my method extensions.
WorkflowMethodExtensions.call(this);
}
function WorkflowMethodExtensions(){
this.setTitle = function(newtitle){this.title = newtitle; return this;};
this.getTitle = function(){return this.title;};
}
Then use:
WorkflowMethodExtensions.call(newWorkflow);
to extend an existing object with those methods defined in the existion