Javascript apply methods from one object to another - javascript

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

Related

Load object with logic

I believe that solution to my issue is relatively easy. I just don't see it.
I have an object:
function MyObject(){
this.attr = "anything";
}
MyObject.prototype.doSomething = function(){
// logic
}
I create the object using new MyObject(). I use it and when I want to quit what I do I simply store it into database (mongodb). In mongo it is stored in this way:
{ "attr" : "anything" }
When I load the object from database I only have the plain object literal without any logic. The methods are missing. I can see why ;) but I don't know how to add the logic to the object literal again...
Question
How can I decorate the retrieved object with it's original logic again? So that it look like this again:
{
"attr" : "anything",
"doSomething": doSomething()
}
How to do it simply?
Is there any other approach to this (except for storing the methods and all prototype hierarchy with it)?
Make a load function to load all the properties of the object you get back into your own object.
You can access your objects property names as an associative array index.
so myobj.attr is the same as myobj['attr'].
This helps with dynamically inserting data into your object whilst keeping full control of the data(my personal favorite) :-)
You can add some extra checks to prevent surplus data or do some extra things whatever you want. For example modifying timestamps.
function MyObject(data){
if(typeof data !== 'undefined') {
this.load(data);
}
else {
this.attr = "anything";
}
}
MyObject.prototype.doSomething = function(){
// logic
}
MyObject.prototype.load = function(data) {
for(var key in data) {
if(data.hasOwnProperty(key)) {
this[key] = data[key];
// Just sample validation check. wahtever you want.
if(key == 'timestamp') {
if(this[key] < new Date().getTime()-4000) {
this[key] = new Date().getTime();
}
}
}
}
}
You can use Object.create to create an object from the prototype without calling the constructor, and then use Object.assign to assign the properties from the object that you retrieved, to the newly created object:
var fullObject = Object.assign( Object.create( MyObject.prototype ), retrievedObject );
Example:
function MyObject(){
this.attr = "anything";
}
MyObject.prototype.doSomething = function(){
document.body.innerHTML = this.attr;
}
// Plain object retrieved from database
var retrievedObject = {
"attr" : "foobar"
};
// Object with proper prototype and properties
var myObject = Object.assign( Object.create( MyObject.prototype ), retrievedObject );
myObject.doSomething();
An extension to #paulpro's answer which I had to modify a little this piece of code Object.create( MyObject.prototype ). As I was using an inherited object this way it didn't instantiate private members properly. I simply replaced Object.create( MyObject.prototype ) with new MyObject(). That's it.
UPDATE 5/3/2016
The safest way is to use lodash's assing.
var objectWithLogic = _.assign(new MyObject(), data);

How to pass custom class instances through Web-Workers?

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.

JavaScript: Adding a Namespace to Prototype Methods

I have a commercial application that has an existing JavaScript object structure using prototype chains. I have had success extending this API by adding more methods to the prototypes of objects. However, I realize that it would be best to add a namespace in front of my methods in case the application vendor decides to name a new method the same as one of my methods in a future release.
If I have an existing object called State, I would add a method called getPop like so:
State.prototype.getPop = function(){return this.pop;};
var Washington = new State('Washington',7000000);
Washington.getPop(); //returns 7000000
What I want to do is add a namespace called 'cjl' before my custom method to avoid name collision so that I can call it like so:
Washington.cjl.getPop();
I tried:
State.prototype.cjl = {};
State.prototype.cjl.getPop = function(){return this.pop;};
The problem is this. It doesn't point to the instance but instead points to the 'cjl' object.
I tried various methods, including using .bind() but none of them seemed to work. I finally found an answer here: Is it possible to organise methods on an object's prototype into namespaces? This works using the Object.defineProperty() method. The problem is the commercial application only works in compatibility mode in IE which doesn't support the Object.defineProperty() method for non-DOM elements.
Is there another way to accomplish this? I don't want to have to call multiple functions, which is the result of some techniques, e.g.:
Washington.cjl().getPop();
You could namespace in the following way, reading your comments I see that you can't change the original constructor so you'll have to replace the original with your own and save the original in a closure.
Every state instance will have it's own cjl instance but that only has a reference to current State instance, all the cjl functions are shared as they exist only once:
[UPDATE]
Forgot to get State.prototype in myState's prototype chain.
//the original constructor
function State(name, pop){
this.name=name;this.pop=pop;
}
State.org="original constructor";
//original constructor is available through
// closure and window.State is replaced with
// your constructor having the cjl namespace
(function(State){
//cjl namespace
function cjl(stateInstance){
this.stateInstance=stateInstance;
};
//cjl functions
cjl.prototype.getPopInThousands=function(){
//do not use this, instead use this.stateInstance
return this.stateInstance.pop/1000;
}
function myState(){
//apply State constructor
State.apply(this,arguments);
//create a clj instance, all methods
// are on cjl.prototype so they're shared
this.cjl = new cjl(this);
}
//inherit from State (use polyfil for older browsers)
myState.prototype = Object.create(State.prototype);
//replace window.State with your constructor
window.State=myState;
}(State))
var s = new State("Wasington", 7000000);
console.log(s.cjl.getPopInThousands());
//non standard, name
console.log("constructor name",s.constructor.name);
console.log("constructor tostring",s.constructor.toString());
More on constructor functions and prototype can be found here: https://stackoverflow.com/a/16063711/1641941
I have to agree with friend and cookie that pre fixing the function names may be the better solution but if you want to use the same methods for an object named Country then you may think of using the previous code as you can re use the cjl object.
Instead of defining State.prototype.cjl outside of the function, try to set the cjl "namespace" inside the constructor function.
function State(){
var thisObject = this;
this.cjl = {
getPop: function(){
return thisObject.pop;
}
};
}
Then you can do Washington.cjl.getPop();.
Try:
var State = function(name, pop) {
this.name = name;
this.pop = pop;
};
State.prototype.cjl = function(method) {
return this.cjlDefs[method].apply(this, Array.prototype.slice.call(arguments, 1) );
};
State.prototype.cjlDefs = {
getPop: function() {return this.pop;}
};
var Washington = new State('Washington', 80000);
console.log( Washington.cjl('getPop') );
https://jsfiddle.net/ghbjhxyh/
Or another shape if you prefer:
var State = function(name, pop) {
this.name = name;
this.pop = pop;
};
State.prototype.cjl = function(method) {
this.cjlDefs.obj = this;
return this.cjlDefs;
};
State.prototype.cjlDefs = {
assertObj: function() { /* Make sensible assertion */ },
getPop: function() { this.assertObj(); return this.obj.pop; }
};
var Washington = new State('Washington', 75000);
console.log( Washington.cjl().getPop() ); // 75000
https://jsfiddle.net/7vjrz2mn/

How to extend Object in JavaScript without losing the original functionality

I have a JavaScript object defined like so:
var Object = (function () {
function Object() {
this.id = RandomNumber();
}
// Custom Object.prototype / Object impementations here...
return Object;
})();
The problem is that once this has been constructed, it loses original functionality like Object.defineProperty etc.
The idea is that I want to extend the basic functionality of Object, not re-write or overwrite the existing prototype.
How can this be achieved?
EDIT: Just to be clear, I know I can do this without affecting the original functionality:
Object.prototype.foo = function() { }
but I need to specifically add functionality to Object's constructor, i.e.
function Object() { this.id = 0; }
The new functionality must not overwrite the original Functionality.
Use the .prototype to add a property:
Object.prototype.specialMethod = function () {
// Your method's code
};
And you'd use it like:
var a = {};
a.specialMethod();
Although I would discourage adding a property to the Object's prototype, because it is enumerable and will mess up looping, and will be inherited by all objects, and objects that inherit from Object, which is basically everything.
You could actually use the Object.defineProperty method you mention:
Object.defineProperty(Object.prototype, "specialMethod", {
enumerable: false, // The important one, to avoid looping problems
configurable: false,
writable: false,
value: function () {
// Your method's code
}
});
Do as Ian wrote. If you also want to check it the method already exists use
if (Object.prototype.specialMethod == null) Object.prototype.specialMethod = function() { ... };
In order to extend this object you should create another object that has its prototype assigned a new instance of Object.
var Object = (function () {
function Object() {
this.id = 5;
}
Object.prototype.speak = function(prop){
alert(this[prop]);
}
return Object;
})();
function ExtendsObject(prop){
this.someProperty = prop;
}
ExtendsObject.prototype = new Object();
var xObj = new ExtendsObject("derived");
xObj.speak("id");
xObj.speak("someProperty");
Working Example: http://jsfiddle.net/RbCcA/
If you want to stick with the self executing functions here is the example rewrote:
var Object = (function () {
function Object() {
this.id = 5;
}
Object.prototype.speak = function(prop){
alert(this[prop]);
}
return Object;
})();
var ExtendsObject = (function(){
function ExtendsObject(prop){
this.someProperty = prop;
}
ExtendsObject.prototype = new Object();
return ExtendsObject;
})();
var xObj = new ExtendsObject("derived");
xObj.speak("id");
xObj.speak("someProperty");
Working Example: http://jsfiddle.net/RbCcA/1/
I do question the use of self executing functions in this situation. They are usually used to encapsulate and shield internals, however in the code example they are being exposed by returning the object from the SEF. Returning the object and storing it in a global variable just re-exposes the object, allowing its prototype and properties to be manipulated. Maybe there are private variables you have not mentioned, but as stated I find the SEFs unnecessary.

how to have Javascript Object creation pattern with Reusable methods and private properties?

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();

Categories