I have several business objects (with different types) that implement functions for a common object, which is assigned to a property of each business object.
It is possible to call the functions declared on business objects from the common object?
Hereafter some similar code:
var Percent= function(){
this.input= null;
...
this.getValuesRange= function(){
return [min,max];
}
...
}
var Speed= function(){
this.input= null;
...
this.getValuesRange= function(){
return [min,max];
}
...
}
var Input= function(){
this.inputRange= function(){
//here I need to call owner's getValuesRange function
}
}
var input= new Input();
var mySpeed= new Speed();
var rocketSpeed= new Speed();
...
mySpeed.input= input;
rocketSpeed.input= input;
...
facade.enter(mySpeed);
facade.enter(rocketSpeed);
...
For inputRange on Input to access getValuesRange, it has to have access to an instance of Speed. Your code has to provide it that instance, there's no built-in "what object references this object via a property" operation in JavaScript. (If there were, it would have to allow for the fact multiple objects can reference the same object, as in your example — both Speed instances reference the same Input instance.)
This is possible with some workarounds. Here an example:
var c = 0;
var Speed= function(){
var mc = c++;
var _input = null;
var _inputRange = null;
this.getInput= function() {
var self = this;
_input.inputRange = function() {
_inputRange.call(self);
}
return _input;
}
this.setInput= function(input) {
_input = input;
_inputRange = input.inputRange;
}
this.getValuesRange= function(){
console.log("Speed", mc);
}
}
var Input= function(){
this.inputRange= function(){
this.getValuesRange()
}
}
var input= new Input();
var mySpeed= new Speed();
var rocketSpeed= new Speed();
mySpeed.setInput(input);
rocketSpeed.setInput(input);
mySpeed.getInput().inputRange(); // Speed 0
rocketSpeed.getInput().inputRange(); // Speed 1
There are many possible pitfalls with this solution. It's here only tho give an idea.
Related
I want to copy the functions and properties of an object into new object. The old object should not effect by changing made in new Object.
Here is the object definition:
var Call = function() {
this.number="123";
}
Call.prototype.function1 = function() {
return this.number;
}
var callobj = new Call();
I can access function1 using callobj.function1().
What I have tried to copy it:
Javascript:
var newcallobj = Object.assign({}, callobj);
In this case, i am not able to access function1 but i can access number property directly.
JQUERY:
var newObj = jQuery.extend(true, {}, callobj); OR
var newObj = jQuery.extend({}, callobj);
In this case, i am able to access function1 and property but when i change number like that newObj.number="222". It also change the value of original object.
I know that there is couple of other posts. But all is not working for me. Please let me know if i am doing any thing wrong?
AFTER #gurvinder372 answer(I am updating question):
After #gurvinder372 answer. It is working for first level of property but if it has another object like i show below and i change the value of property of another object. Then it is effecting on original object also.
var ABC = function(){
this.number = "333";
}
var Call = function() {
this.number="123";
this.anotherobj = new ABC();
}
Call.prototype.function1 = function() {
return this.number;
}
var callobj = new Call();
var newcallobj = Object.create(callobj);
newcallobj.anotherobj.number= "123";
console.log(newcallobj.anotherobj.number);
console.log(callobj.anotherobj.number);
Output of both is 123. #gurvinder372. can you check th above code ?
Object.assign only copies the enumerable properties of an object.
Use Object.create instead of Object.assign
var newcallobj = Object.create(callobj);
var Call = function() {
this.number="123";
}
Call.prototype.function1 = function() {
return this.number;
}
var callobj = new Call();
var newcallobj = Object.create(callobj);
console.log(newcallobj.function1());
Ok. By the help of #gurvinder372. The following solution is working for me.
var ABC = function(){
this.number = "333";
}
var Call = function() {
this.number="123";
this.anotherobj = new ABC();
}
Call.prototype.function1 = function() {
return this.number;
}
var callobj = new Call();
var newcallobj = Object.create(callobj);
newcallobj.anotherobj = Object.create(callobj.anotherobj);
newcallobj.anotherobj.number= "123";
console.log(newcallobj.anotherobj.number);
console.log(callobj.anotherobj.number);
Please let me know if there is any better solution other than this?
Hi I'm trying to implement a LinkedList in Javascript. When i assign a value to my node it doesn't seem to store it when I use my getter. For example:
var Node =function() {
var _data;
var _next ={};
var that = this;
that.getData = function() {
return _data;
};
that.setData = function(data) {
that._data = data;
};
that.getNext = function() {
return _next;
};
that.setNext = function(next) {
that._next = next;
};
return that;
};
Will not work with:
var nodeObj = new Node();
nodeObj.setData("hello");
console.log(nodeObj.getData());
_data is not the same as that._data, you must do this:
that.getData = function() {
return that._data;
};
OR you could do this instead:
that.setData = function(data) {
_data = data;
};
the benefit of the second approach being that you're simulating a private variable (because you cannot do nodeObj._data in the second case but you can in the first)
also var that = this; is unnecessary, you can simply do this._data in this case.
For your case here, you can assume that if you're calling a function like yourObject.someFunction(), then within someFunction the value of this equals yourObject. (And this isn't always true in javascript but since you're starting off you should think about it this way for now. If you pass a function to another function as a variable and then call it then this wouldn't be the case).
I am in the final stages of a game development and i have a bunch of objects like this;
roomBedroom = function () {
this.title = "Bedroom";
this.description = "I'm in a bedroom";
this.noun = "bed";
this.entities = new Array();
}
var bedroom = new roomBedroom();
What I want to do now is place all of my game objects into an array;
var savedGameObjects = {};
savedGameObjects['bedroom'] = bedroom;
var jsonGame = JSON.stringify(savedGameObjects);
The plan is to then save the savedGameObjects array and then recall it when the user loads the game again.
If I replace savedGameObjects['bedroom'] = bedroom; with savedGameObjects['bed'] = 'slappy'; it works but not when I have the object.
I really need to save the objects in their current state. I'd rather not go through each object saving key pieces of information one by one.
This feels like a bit of a hack, but its the best I can come up with right now
Your serialization/deserializtion utility
This is going to attach obj.constructor.name to obj.__prototype before serialization. Upon deserializing, the prototype will be put back in place.
(function(global) {
function serialize(obj) {
obj.__prototype = obj.constructor.name;
return JSON.stringify(obj);
};
function deserialize(json) {
var obj = JSON.parse(json);
obj.__proto__ = global[obj.__prototype].prototype;
return obj;
}
global.serialize = serialize;
global.deserialize = deserialize;
})(window);
A sample "class"
(function(global) {
function Foo() {
this.a = "a";
this.b = "b";
}
Foo.prototype.hello = function() {
console.log("hello");
}
global.Foo = Foo;
})(window);
Let's try it out
var foo = new Foo();
var json = serialize(foo);
console.log(json);
var newFoo = deserialize(json);
console.log('a', newFoo.a); // a
console.log('b', newFoo.b); // b
newFoo.hello(); // hello
Watch out for some gotchas
If you use an expression to define your "class", you will have a nameless constructor
var Foo = function() {};
var foo = new Foo();
foo.constructor.name; // ""
As opposed to a named function
function Foo() {}
var foo = new Foo();
foo.constructor.name; // Foo
In order for serialize and deserialize to work, you will need to use named functions
Another gotcha
The deserialize method expects your "classes" to exist on the in the same namespace (window in this case). You could encapsulate your game object classes in another way, just make sure that you reconfigure the deserialize method so that it can find the prototypes as needed.
Making this better
Instead of attaching serialize to the global window, you could have serialize live on (e.g.) the GameObject.prototype then your individual classes could inherit from GameObject. Serializing an object would then be as simple as
var json = foo.serialize();
// {"a":"a","b":"b","__prototype":"Foo"}
You could then define deserialize as GameObject.deserialize and restoring foo would be
var foo = GameObject.deserialize(json);
An alternative solution
Instead of implementing a custom serializer and deserializer, you could make very clever use of the Factory Method Pattern.
This might be a little verbose, but it does give you individual control over how a game object should be deserialized/restored.
var savedData = // your normal JSON here
var player = Player.create(savedData.player);
var items = [];
for (var i=0, i<savedData.items.length; i++) {
items.push(Item.create(savedData.items[i]));
}
var map = Map.create(savedData.map);
This was a pretty interesting problem and I'm sure you're not the first to encounter it. I'm really curious to see what other people come up with.
If I run the following code in a browser there is no problem getting the JSON string of the bedroom object, not sure what the problem is.
Note that JSON is data and bedroom is an object, bedroom may have behaviour like turnOffLight() that JSON doesn't have.
roomBedroom = function () {
this.title = "Bedroom";
this.description = "I'm in a bedroom";
this.noun = "bed";
this.entities = new Array();
}
var bedroom = new roomBedroom();
var savedGameObjects = {};
savedGameObjects['bedroom'] = bedroom;
//logs {"bedroom":{"title":"Bedroom","description":
// "I'm in abedroom","noun":"bed","entities":[]}}
console.log(JSON.stringify(savedGameObjects));
So if you want to re create object instances from JSON data then you can change your constructor:
roomBedroom = function (args) {
//following fails fast and loud, you could silently
//fail by setting args to {}
if(typeof args!=="object")
throw new Error("Have to create roomBedroom by passing an object");
//or do args={} to silently fail
this.title = args.title||"Bedroom";
this.description = args.description||"I'm in a bedroom";
this.noun = args.noun||"bed";
//if entities are objects with behavior
// you have to re create them here passing the JSON data
// as I've done with roomBedroom
this.entities = args.entities||new Array();
}
var jsonString='{"bedroom":{"title":"Bedroom",'+
'"description":"I\'m in a bedroom",'+
'"noun":"bed","entities":[]}}';
var bedroom = new roomBedroom({});
bedroom.entities.push({hi:"there"});
bedroom.title="Master Bedroom";
//serialize bedroom to a json string
var jsonString = JSON.stringify(bedroom);
//create a roomBedroom instance named br2 using
// the serialized string
var br2=new roomBedroom(JSON.parse(jsonString));
//compare if they are the same
console.log(JSON.stringify(bedroom)===JSON.stringify(br2));//true
I have an approach that might work for you. You can see it in action on JSFiddle.
The main point is to use the reviver parameter to JSON.parse to reconstruct your object when it's parsed.
I do this with a general-purpose reviver that can be configured for multiple different types, although here the only one used is the RoomBedroom constructor. This implementation assumes that you have simple copy constructors that create new objects using a reference to an existing one. (For other, more sophisticated possibilities, see an answer to another question I gave in February.) To make it easy to have a copy constructor, I have one more function that accepts a very simple constructor function and a set of default values and builds a copy constructor function for you.
var MultiReviver = function(types) {
return function(key, value) {
var type;
for (var i = 0; i < types.length; i++) {
type = types[i];
if (type.test(value)) {
return new type.constructor(value);
}
}
return value;
};
};
var makeCloningConstructor = (function() {
var clone = function(obj) {return JSON.parse(JSON.stringify(obj));};
var F = function() {};
return function(Constructor, defaults) {
var fn = function(obj) {
Constructor.call(this);
var self = this;
var config = obj || {};
Object.keys(defaults).forEach(function(key) {
self[key] = clone(defaults[key]);
});
Object.keys(config).forEach(function(key) {
self[key] = clone(config[key]);
});
};
F.prototype = Constructor.prototype;
fn.prototype = new F();
fn.constructor = Constructor;
return fn;
};
})();
// Note: capitalize constructor functions
var RoomBedroom = makeCloningConstructor(function RoomBedroom() {}, {
title: "Bedroom",
description: "I'm in a bedroom",
noun: "bed",
entities: [] // Note: use `[]` instead of `new Array()`.
});
RoomBedroom.prototype.toggleLight = function() {
this.lightOn = !this.lightOn;
};
RoomBedroom.prototype.checkLights = function() {
return "light is " + (this.lightOn ? "on" : "off");
};
var bedroom = new RoomBedroom();
bedroom.windowCount = 3; // add new property
bedroom.noun = "king-sized bed"; // adjust property
bedroom.toggleLight(); // create new propery, use prototype function
console.log(bedroom.checkLights());
var savedGameObjects = {};
savedGameObjects['bedroom'] = bedroom;
var jsonGame = JSON.stringify(savedGameObjects);
var reviver = new MultiReviver([{
constructor: RoomBedroom,
test: function(obj) {
var toString = Object.prototype.toString, str = "[object String]",
arr = "[object Array]";
return toString.call(obj.title) == str &&
toString.call(obj.description) == str &&
toString.call(obj.noun) == str &&
toString.call(obj.entities) == arr;
}
}]);
var retrievedGameObjects = JSON.parse(jsonGame, reviver);
// data comes back intact
console.log(JSON.stringify(retrievedGameObjects, null, 4));
// constructor is as expected
console.log("Constructor: " + retrievedGameObjects.bedroom.constructor.name);
// prototype functions work
console.log(retrievedGameObjects.bedroom.checkLights());
I don't know if it's precisely what you were looking for, but I think it's at least an interesting approach.
the faster route
It is better — from an optimisation point of view — to do as Adeneo states, which is power each of your Game Objects by an exportable simple object i.e:
roomBedroom = function(){
this.data = {};
this.data.title = 'Bedroom'
/// and so on...
}
These can then be easily stored and re-imported just by JSON.Stringifying and overwriting the data property. For example, you could set-up the system that Maček mentions (+1) which is to give each of your game objects serialize and deserialize functions:
roomBedroom.prototype.serialize = function(){
return JSON.stringify( this.data );
};
roomBedroom.prototype.deserialize = function( jstr ){
this.data = JSON.parse(jstr);
};
the quicker way
However, you can make a simple addition to what you already have using the following:
First enhance your Game Objects with an objectName property. This is because constructor.name and function.name are unreliable and do strange things the further back in time you go, far better to use a string you have set in stone.
var roomBedroom = function ( title ) {
this.objectName = "roomBedroom";
this.title = title;
this.description = "I'm in a bedroom";
this.noun = "bed";
this.entities = new Array();
};
Then the additional code to help with storage:
var storage = {};
/// add your supported constructors to this list, there are more programmatic
/// ways to get at the constructor but it's better to be explicit.
storage.constructors = {
'roomBedroom' : roomBedroom
};
/// take an instance and convert to simple object
storage.to = function( obj ){
if ( obj.toStorage ) {
return obj.toStorage();
}
else {
var keep = {};
for ( var i in obj ) {
if ( obj.hasOwnProperty(i) && !obj[i].call ) {
keep[i] = obj[i];
}
}
return keep;
}
}
/// take simple object and convert to an instance of constructor
storage.from = function( obj ){
var n = obj && obj.objectName, c = storage.constructors[n];
if ( n && c ) {
if ( c.fromStorage ) {
return c.fromStorage( obj );
}
else {
var inst = new c();
for ( var i in obj ) {
if ( obj.hasOwnProperty(i) ) {
inst[i] = obj[i];
}
}
return inst;
}
}
else {
throw new Error('`' + n + '` undefined as storage constructor');
}
}
Once you have that you can use it like so:
var savedGameObjects = {};
savedGameObjects['bedroom'] = storage.to(new roomBedroom("bedroom"));
savedGameObjects['bedroom2'] = storage.to(new roomBedroom("bedroom2"));
var jsonGame = JSON.stringify(savedGameObjects);
console.log(jsonGame);
savedGameObjects = JSON.parse(jsonGame);
for( var i in savedGameObjects ) {
savedGameObjects[i] = storage.from(savedGameObjects[i]);
console.log(savedGameObjects[i]);
}
extras
You can also be specific about the way objects get stored/unstored by supplying toStorage and fromStorage methods on your constructed instances and constructors respectively. For example, you could use the following if you only wanted to store titles of roomBedrooms. Obviously this is an unrealistic use-case, you'd more often use this to avoid storing cached or computed sub-objects and properties.
roomBedroom.prototype.toStorage = function( obj ){
var ret = {};
ret.title = obj.title;
return ret;
};
roomBedroom.fromStorage = function( obj ){
var inst = new roomBedroom();
inst.title = obj.title;
return inst;
};
The above also means you can take advantage of improving your Game Object construction by providing parameters, rather than iterating over properties which can be slow and error-prone.
roomBedroom.fromStorage = function( obj ){
return new roomBedroom( obj.title );
};
Or even:
roomBedroom.fromStorage = function( obj ){
return new roomBedroom( obj ); // <-- the constructor processes the import.
};
fiddle
http://jsfiddle.net/XTUdp/
disclaimer
The above code relies on the existence of hasOwnProperty which is not present cross-browser yet, a polyfill should be used until it is... or, if you aren't doing anything complicated with prototype inheritance you don't need to worry and can remove it from the code.
you can declare a big variable like
var world = {};
and each small variable declare as
var bedroom = world.bed = (world.bed || new roomBedroom());
remember never change bedroom to another object, i think this will work fine, but looks too long winded
My JS is organized into viewmodels and services. The services are mostly concerned with AJAX calls, whereas my viewModels describe the view that they're used in.
I have two view models now - StoreViewModel, and MyStoreViewModel. In each of these, I have the following:
function MyStoreVm(model) {
var self = this;
self.doThis = function(){
// do stuff
self.doThat();
};
}
Then:
function StoreVm(model) {
var self = this;
self.doThis = function(){
// do stuff
self.doThat();
};
}
I come from a C# background - normally I would just use inheritance in this kind of situation. How can I eliminate this code repetition between two distinct modules / viewmodels, by having them inherit from a third, shared module?
More details: These are being used in an MVC view where I have knockout bindings depending on whether or not the store is MyStore:
#if (!Model.IsMyStore) {
<script type="text/javascript">
$(document).ready(ko.applyBindings(new StoreVm(#Html.Raw(JsonConvert.SerializeObject(Model, new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver() })))));
</script>
} else if (Model.IsMyStore) {
<script type="text/javascript">
$(document).ready(ko.applyBindings(new MyStoreVm(#Html.Raw(JsonConvert.SerializeObject(Model, new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver() }).Sanitize()))));
</script>
}
UPDATE
I looked into some of the suggestions below, but none seemed clean and simple enough for my novice skills. I tried the following which seems to work:
function BaseStore(model){
self.doThis = function(){
// do stuff
self.doThat();
};
// and a whole lot of other shared code
}
function StoreVm(model) {
var storeVm = new BaseStoreVm(model)
var self = storeVm;
self.isolatedFunctionForGenericStores = function(){stuff}
// other stuff for only this type
return storeVm;
}
function MyStoreVm(model) {
var myStoreVm = new BaseStoreVm(model)
var self = myStoreVm;
self.isolatedFunctionForMyStore = function(){stuff}
// other stuff for only this type
return myStoreVm;
}
Is there anything wrong with this approach?
If you have two child types that need to inherit from the same parent, you can do:
function Parent( foo ) {
this.foo = foo;
}
Parent.prototype.method = function() {
console.log( this.foo );
};
function Child1() {
Parent.call( this, "bar" );
}
Child1.prototype = Object.create( Parent.prototype );
Child1.prototype.constructor = Child1;
function Child2() {
Parent.call( this, "qux" );
}
Child2.prototype = Object.create( Parent.prototype );
Child2.prototype.constructor = Child2;
var parent = new Parent("blah");
var child1 = new Child1();
var child2 = new Child2();
parent.method(); // => "blah"
child1.method(); // => "bar"
child2.method(); // => "qux"
First you should understand how JavaScript implement inheritance. JavaScript is a prototype-based language which contains no class statement, such as is found in C#. Instead, it uses functions as classes(no classes, just objects).
So what we have here is objects inherit from other objects (now you might need to get some coffee).
So then JavaScript does not give you the full power of inheritance and polymorphism you get in C#.
If you want to know ways to implement inheritance in JS:
SO: Performing inheritance in JavaScript
My Blog: Javascript Inheritance techniques
Back to your question, i think you may need to implement The Factory Pattern. So your js code could be like that:
function MyStoreVm(model) {
var self = this;
self.doThis = function() {
// do stuff
self.doThat();
};
}
function StoreVm(model) {
var self = this;
self.doThis = function() {
// do stuff
self.doThat();
};
}
// Define factory object that create your proper store object
// StoreFactory takes the model as input.
// You can change it to accept seconf parameter that define class type
function StoreFactory() {
this.classType == "MyStoreVm"; // default value
this.createStore = function(model) {
if (model.IsMyStore === true)
this.classType = MyStoreVm;
else
this.classType = StoreVm;
return new this.classType(model);
}
}
Then in your MVC view:
$(document).ready(function() {
var mystoreFactory = new StoreFactory();
ko.applyBindings(mystoreFactory.createStore((#Html.Raw(JsonConvert.SerializeObject(Model, new JsonSerializerSettings() {
ContractResolver = new CamelCasePropertyNamesContractResolver()
})))));
});
Check out Klass.js. While this is basically the same as creating your own prototypes and inheritance methods, it's nice to use. It's also AMD aware.
// base class
var Store = klass(function() {
var self = this;
// add properties here
}).methods({
doThis: function () {
// do this
},
doThat: function () {
// do that
}
});
return Store;
// create the first constructor
var myStoreVm = Store.extend(function () {
// super class is called
}).methods({
doThis: function(){
this.supr(); // call Store.doThis
// some other code
}
});
return myStoreVm;
// create the second constructor
var storeVm = Store.extend(function () {
// super class is called
}).methods({
doThis: function(){
// override Store.doThis with my own code
}
});
return storeVm;
I have a part of plugin which uses private variables and exposes public method :
JSBIN 1
function myWorld()
{
var myPrivate=1;
this.do1=function (){alert(myPrivate);} ;
}
var a=new myWorld();
a.do1() //1
alert(a.myPrivate); //undefined (as it should be)
But I want to prevent doing this again : new myWorld();
The only option I know is with object literal :
JSBIN 2
var myWorld=
{
myPrivate:1,
do1:function (){alert(this.myPrivate);}
}
alert(myWorld.myPrivate); //1 ( ouch....)
myWorld.do1() //1
Question
How can encapsulate private fields and still prevent uses from myWorld to be instanced >1 times ?
Closures are a great tool to define the scope:
var myWorld= (function(){
var myPrivate = 1;
return {
do1:function (){alert(myPrivate);}
}
}());
myWorld.do1();
You might want to check out the free Learning JavaScript Design Patterns book
Try something along these lines:
(function(){
var instanciated = false;
window.myWorld = function() {
if( instanciated) throw new Error("myWorld can only be instanciated once!");
instanciated = true;
var myPrivate = 1;
this.do1 = function(){alert(myPrivate);};
}
})();
You can hide the private variable inside an IIFE:
var myWorld = (function() {
var myPrivate = 1;
return { ... };
}());
var a = new function myWorld()
{
var myPrivate=1;
this.do1=function (){alert(myPrivate);} ;
}
This makes myWorld available only inside the function. If you don't event want it accessable there, then remove the name.
You could use a singleton pattern to maintain one instance of the object. Something like:
(function (global) {
var _inst;
global.myWorld = function () {
if (_inst) throw new Error("A myWorld instance already exists. Please use myWorld.getInstance()");
_inst = this;
};
global.myWorld.prototype = {
do1: function() {
console.log("do1");
}
};
global.myWorld.getInstance = function() {
if (_inst) return _inst;
return new myWorld();
};
}(window));
var world = new myWorld();
var world2 = myWorld.getInstance();
console.log(world === world2); // true
var world3 = new myWorld(); // throws Error