"OOP"-JS - set propertie equal to function - javascript

Basicly, i want "theId" to be equal to what "getId" returns. But how?
var Quiz = {
getId : function() {
var params = 1;
return params;
},
theId : getId(),
};

An object literal can't refer to properties/methods within itself, because at the time the object literal is evaluated the object doesn't exist yet. You have to do it as a two-step process:
var Quiz = {
getId : function() {
var params = 1;
return params;
}
};
Quiz.theId = Quiz.getId();
Note that that sets theId to whatever getId() returned at that time, it doesn't somehow automatically update theId if your real-world function is more dynamic than your example and potentially returns different values each time it's called.
Alternatively if the function is declared before the object you can create both object properties at once, with one being a reference to the function and the other being the result of calling it.

Seems a bit like a duplicate of Self-references in object literal declarations, but you could use a simple getter function for your property:
var Quiz = {
get theID() {
var params = 1;
return params;
}
};

Related

Override JavaScript (window) Function

I'd like to override a function which is being made by a javascript plugin. I know how to override regular window functions, but this is different. I'm not sure how to name it, but the structure of that function is like:
window.something.function
I have no idea how to override that. I have tried the following:
var originalFunction = window.something.function;
window.something.function = function(parameter) {
alert('called');
return originalFunction(parameter);
}
But it's not working.
Does someone know a solution?
Edit:
As I have been told my question is unclear, I have edited it again using the actual names of the plug-in.
The plugin is meant to be used as:
var myColor = new jscolor(target, options)
When this is being used, there is a function "inside" the object "jscolor" which is being called when setting the value of target element. I want to override that function to add an extra functionality without changing the original js file.
Code:
if (!window.jscolor) { window.jscolor = (function () {
var jsc = {
.....
jscolor : function (targetElement, options) {
....
//Function I want to change:
this.exportColor = function (flags) {
if (!(flags & jsc.leaveValue) && this.valueElement) {
var value = this.toString();
if (this.uppercase) { value = value.toUpperCase(); }
if (this.hash) { value = '#' + value; }
if (jsc.isElementType(this.valueElement, 'input')) {
this.valueElement.value = value;
} else {
this.valueElement.innerHTML = value;
}
}
}
}
};
My attempts so far:
var origJsColor = jscolor.exportColor;
jscolor.exportColor = function(flags) {
console.log('called');
return origJsColor(flags);
}
and the window attempt above.
The jscolor code you've shown creates an object with its own copy of exportColor (one is created for each object). So to replace it, you have to replace it on each instance as the instance is created.
You can do that as a one-off in much the way you showed, just working with the instance rather than the plugin function, and using Function#call to call it with the right this:
// Get the instance
var c = new jscolor(target, options)
// Update it
var origExportColor = c.exportColor;
c.exportColor = function(flags) {
console.log('called');
return origExportColor.call(c, flags); // Note the changes on this line
};
Or instead of
return origExportColor.call(c, flags);
you might use
return origExportColor.apply(c, arguments);
...if there's any chance of the function being called with anything other than exactly one argument. (arguments is a magic pseudo-array containing the arguments used to call the function.)
If you want to do that for all instance you might create, you can put a facade in front of jscolor to do that to each instance:
var realJscolor = jscolor;
jscolor = function() {
// Call the real function, passing along all the arguments we
// get automatically (`arguments` is a magic pseudo-array)
var retVal = realJscolor.apply(this, arguments);
// If it returned a non-`null` object, we want to use that instead
// of `this`; if not, we keep using `this`
if (!retVal || typeof retVal !== "object") {
retVal = this;
}
// Slip in our version of exportColor
var origExportColor = retVal.exportColor;
retVal.exportColor = function(flags) {
console.log('called');
// (Maybe use `apply` here instead)
return origExportColor.call(retVal, flags);
};
// Return the result, in case the real function overrode `this`
return retVal;
};
jscolor.prototype = realJscolor.prototype;
Then just use jscolor normally:
var c = new jscolor(target, options);
The reason for the retVal thing is that although normally a new expression's result is a refernece to the new object created by new, a constructor function can return a non-null object reference and, if it does, the new expression's result is that object reference instead. That's why we check the return value of realJscolor.
Of course, that means that all uses of jscolor on the page that use the global will now use your updated function instead. If you don't want that, just use your own name and don't override jscolor:
var myColor = function() {
var retVal = jscolor.apply(this, arguments);
// ...and so on...
return retVal;
};
myColor.prototype = jscolor.prototype;
Usage:
var c = new myColor(target, options);
Function
function a() {alert(this)} // will print `window` obejct
is defined in the window scope. That is, it is a method of the window. Your more difficult situation comes from the fact that this is different from window if you define function as a method in another object.
var a = {method: function() {alert(this)}}
you call a.method() but see that the same window again. You need to bind your function to the parent object to make it compete method.

Unexpected behavior using getters and setters

Look this code:
<script>
function dbg (object) {
var _string = "";
for (var a in object) {
_string += a + ":\n";
for (var b in object[a])
if (/^get_/.test (b))
_string += "\t" + b + " - " + object[a][b] () + "\n";
}
return _string;
}
function Order () {
var products = [];
this.get_products = function () {return products;}
this.set_products = function (_products) {products = _products;}
}
function Product () {
var id = null;
var name = null;
this.get_id = function () {return id;}
this.get_name = function () {return name;}
this.set_id = function (_id) {id = _id;}
this.set_name = function (_name) {name = _name}
}
var order = new Order ();
var product = new Product ();
product.set_id (1);
product.set_name ("Banana");
order.set_products (order.get_products ().concat (product));
alert (dbg (order.get_products ())); // Ok
product.set_id (2);
product.set_name ("Orange");
order.set_products (order.get_products ().concat (product));
alert (dbg (order.get_products ())); // Duplicated values! What?
</script>
The first time you push the object "Product" into the object "Order", everything looks fine.
When you set new values to the object "Product", the object itself overwrites the previous values of the object "Order". The final result is a array of duplicated values. Is it normal ? Is there a workaround? Just tried everything I knew without success. Thanks.
Crazy Train has already answered it in the comments. The question is listed having 0 answers so I'll add it as an answer.
When adding a variable containing an object to an array you add a reference to the variable, when you re assign the variable the reference is broken.
Adding a variable containing an object to an array then re assigning the variable doesn't change the object in the array:
var arr=[];
var object={name:"John"};
arr.push(object);
object=33;
console.log(arr);//=[Object {name="john"}]
Adding a variable containing an object to an array then changing the internal values of the object that the variable contains does change the object in the array:
var arr=[];
var object={name:"John"};
arr.push(object);
object.name="Jane";
console.log(arr);//=[Object {name="Jane"}]
So to correct your code you could do the following:
Create a new variable for the product to be added:
var product2=new Product();
product2.set_id (2);
product2.set_name ("Orange");
order.set_products (order.get_products ().concat (product2));
Or break the reference between your product variable and the products array in order:
product=null;//product has no ref to order.products
product=new Product();
product.set_id (2);
product.set_name ("Orange");
order.set_products (order.get_products ().concat (product));
I would not define members of an object in a constructor function with var as JavaScript doesn't support private members. You can simulate them by creating closures but that has it's own problem when you have instance specific privates (as is your case). You can't use prototype if the functions need to access private instance variables, you can't clone it unless you have public accesssors, inheritance and overriding functions will be a pain.
Here is some more info on using constructor functions.
If you have Chrome or Firefox (with Firebug) then you can press F12 to open the console. You an detach the console window (have it's own window) then copy code in the before mentioned answers and paste them in the commandline of the console. There you can run and re run the code, change and see the output to better understand JS behavior.
You are just overriding the variables in object. I'd do it like this, much simpler:
var products = {
set : function(name,id) {
products.list.push({name:name,id:id});
},
get : function(id) {
var r;
if(typeof id === 'number'){
products.list.forEach(function(e,i){ if(e.id==id) r= products.list[i];});
} else {
products.list.forEach(function(e,i){ if(e.name==id) r = products.list[i];});
}
return r;
},
list : []
};
var order={
set : function(p) {
order.list[p.id]=p;
},
get : function(id) {
return order.list[id];
},
delete : function(id) {
return delete order.list[id];
},
list : {}
};
then you can do this
products.set('apple',34);
products.set('orange',4);
products.set('mango',1);
var x = products.get(1);
var y = products.get('orange');
order.set(x);
order.set(y);
working demo:
http://jsfiddle.net/techsin/tjDVv/2/

Using JSON.stringify on custom class

I'm trying to store an object in redis, which is an instance of a class, and thus has functions, here's an example:
function myClass(){
this._attr = "foo";
this.getAttr = function(){
return this._attr;
}
}
Is there a way to store this object in redis, along with the functions? I tried JSON.stringify() but only the properties are preserved. How can I store the function definitions and be able to perform something like the following:
var myObj = new myClass();
var stringObj = JSON.stringify(myObj);
// store in redis and retreive as stringObj again
var parsedObj = JSON.parse(stringObj);
console.log(myObj.getAttr()); //prints foo
console.log(parsedObj.getAttr()); // prints "Object has no method 'getAttr'"
How can I get foo when calling parsedObj.getAttr()?
Thank you in advance!
EDIT
Got a suggestion to modify the MyClass.prototype and store the values, but what about something like this (functions other than setter/getter):
function myClass(){
this._attr = "foo";
this._accessCounts = 0;
this.getAttr = function(){
this._accessCounts++;
return this._attr;
}
this.getCount = function(){
return this._accessCounts;
}
}
I'm trying to illustrate a function that calculates something like a count or an average whenever it is called, apart from doing other stuff.
First, you are not defining a class.
It's just an object, with a property whose value is a function (All its member functions defined in constructor will be copied when create a new instance, that's why I say it's not a class.)
Which will be stripped off when using JSON.stringify.
Consider you are using node.js which is using V8, the best way is to define a real class, and play a little magic with __proto__. Which will work fine no matter how many property you used in your class (as long as every property is using primitive data types.)
Here is an example:
function MyClass(){
this._attr = "foo";
}
MyClass.prototype = {
getAttr: function(){
return this._attr;
}
};
var myClass = new MyClass();
var json = JSON.stringify(myClass);
var newMyClass = JSON.parse(json);
newMyClass.__proto__ = MyClass.prototype;
console.log(newMyClass instanceof MyClass, newMyClass.getAttr());
which will output:
true "foo"
No, JSON does not store functions (which would be quite inefficient, too). Instead, use a serialisation method and a deserialisation constructor. Example:
function MyClass(){
this._attr = "foo";
this.getAttr = function(){
return this._attr;
}
}
MyClass.prototype.toJSON() {
return {attr: this.getAttr()}; // everything that needs to get stored
};
MyClass.fromJSON = function(obj) {
if (typeof obj == "string") obj = JSON.parse(obj);
var instance = new MyClass;
instance._attr = obj.attr;
return instance;
};
Scanales, I had the same issue and tried a technique similar to Bergi's recommendation of creating new serialization/deserialization methods...but found it didn't work for me because I have objects nested in objects (several deep). If that's your case then here's how I solved it. I wrote a base class (clsPersistableObject) from which all objects that I wanted to persist inherited from. The base class has a method called deserialize, which is passed the JSON string. This method sets the properties one by one (but does not wipe out the exist methods) and then recursively defer to the child object to do the same (as many times as necessary).
deserialize: function (vstrString) {
//.parse: convert JSON string to object state
//Use JSON to quickly parse into temp object (does a deep restore of all properties)
var tmpObject = JSON.parse(vstrString);
//objZoo2.animal.move();
//Note: can't just do something like this:
// CopyProperties(tmpObject, this);
//because it will blindly replace the deep objects
//completely...inadvertently wiping out methods on it. Instead:
//1) set the properties manually/one-by-one.
//2) on objects, defer to the deserialize on the child object (if it inherits clsPersistableObject)
//2b) if it doesn't inherit it, it's an intrinsic type, etc...just do a JSON parse.
//loop through all properties
var objProperty;
for (objProperty in tmpObject) {
//get property name and value
var strPropertyName = objProperty;
var strPropertyValue = tmpObject[objProperty]; //note: doing this .toString() will cause
if (objProperty !== undefined) {
//check type of property
if (typeof strPropertyValue == "object") {
//object property: call it recursively (and return that value)
var strPropertyValue_AsString = JSON.stringify(strPropertyValue);
//see if has a deserialize (i.e. inherited from clsPeristableObject)
if ("deserialize" in this[objProperty]) {
//yes: call it
this[objProperty]["deserialize"](strPropertyValue_AsString);
}
else {
//no: call normal JSON to deserialize this object and all below it
this[objProperty] = JSON.parse(strPropertyValue_AsString);
} //end else on if ("deserialize" in this[objProperty])
}
else {
//normal property: set it on "this"
this[objProperty] = tmpObject[objProperty];
} //end else on if (typeof strPropertyValue == "object")
} //end if (objProperty !== undefined)
}
}
it looks like you attempt to stringify a closed function. you can use ()=>{} to solve the scope problem.
function myClass(){
this._attr = "foo";
this._accessCounts = 0;
this.getAttr = ()=>{
this._accessCounts++;
return this._attr;
}
this.getCount = ()=>{
return this._accessCounts;
}
}
What you get back grom JSON.stringify() is a String. A string has no methods.
You need to eval first that string and then you'll be able to get the original object
and its methods.
var myObj = new myClass();
var stringObj = JSON.stringify(myObj);
---- EDIT -----
//Sorry use this:
var getBackObj = JSON.parse(stringObj);
//Not this
var getBackObj = eval(stringObj);
console.log(getBackObj.getAttr()); // this should work now

Javascript: How to turn a JSON array of object back in to the Object type sharing prototypes?

If you have an array of product objects created from JSON, how would you add a prototype method to the product objects so that they all point to the same method? How would you train JavaScript to recognize all product objects in an array are instances of the same class without recreating them?
If I pull down a JSON array of Products for example, and want each product in the array to have a prototype method, how would I add the single prototype method to each copy of Product?
I first thought to have a Product constructor that takes product JSON data as a parameter and returns a new Product with prototypes, etc. which would replace the data send from the server. I would think this would be impractical because you are recreating the objects. We just want to add functions common to all objects.
Is it possible to $.extend an object's prototype properties to the JSON object so that each JSON object would refer to exactly the same functions (not a copy of)?
For example:
var Products = [];
Products[0] = {};
Products[0].ID = 7;
Products[0].prototype.GetID = function() { return this.ID; };
Products[1].ID = 8;
Products[1].prototype = Products[0].prototype; // ??
I know that looks bad, but what if you JQuery $.extend the methods to each Product object prototype: create an object loaded with prototypes then $.extend that object over the existing Product objects? How would you code that? What are the better possibilities?
For one, you're not modifying the Products[0].prototype, you're modifying Object.prototype, which will put that function on the prototype of all objects, as well as making it enumerable in every for loop that touches an Object.
Also, that isn't the proper way to modify a prototype, and ({}).prototype.something will throw a TypeError as .prototype isn't defined. You want to set it with ({}).__proto__.something.
If you want it to be a certain instance you need to create that instance, otherwise it will be an instance of Object.
You probably want something like:
var Product = function(ID) {
if (!this instanceof Product)
return new Product(ID);
this.ID = ID;
return this;
};
Product.prototype.GetID = function() {
return this.ID;
};
Then, fill the array by calling new Product(7) or whatever the ID is.
First, one problem is that prototype methods are associated when the object is created, so assigning to an object's prototype will not work:
var Products = [];
Products[0] = {};
Products[0].prototype.foo = function () { return 'hello' } // ***
Products[0].foo(); // call to undefined function
(*** Actually, the code fails here, because prototype is undefined.)
So in order to attach objects, you'll need to assign actual functions to the object:
Products[0].foo = function () { return 'hello'; };
You can create a helper function to do so:
var attachFoo = (function () { // Create a new variable scope, so foo and
// bar is not part of the global namespace
function foo() { return this.name; }
function bar() { return 'hello'; }
return function (obj) {
obj.foo = foo;
obj.bar = bar;
return obj; // This line is actually optional,
// as the function /modifies/ the current
// object rather than creating a new one
};
}());
attachFoo(Products[0]);
attachFoo(Products[1]);
// - OR -
Products.forEach(attachFoo);
By doing it this way, your obj.foos and obj.bars will all be referencing the same foo() and bar().
So, if I'm getting this all correctly, this is a more complete example of KOGI's idea:
// Create a person class
function Person( firstName, lastName ) {
var aPerson = {
firstName: firstName,
lastName: lastName
}
// Adds methods to an object to make it of type "person"
aPerson = addPersonMethods( aPerson );
return aPerson;
}
function addPersonMethods( obj ) {
obj.nameFirstLast = personNameFirstLast;
obj.nameLastFirst = personNameLastFirst;
return obj;
}
function personNameFirstLast() {
return this.firstName + ' ' + this.lastName;
}
function personNameLastFirst() {
return this.lastName + ', ' + this.firstName;
}
So, with this structure, you are defining the methods to be added in the addPersonMethods function. This way, the methods of an object are defined in a single place and you can then do something like this:
// Given a variable "json" with the person json data
var personWithNoMethods = JSON.parse( json ); // Use whatever parser you want
var person = addPersonMethods( personWithNoMethods );
You could do this...
function product( )
{
this.getId = product_getId;
// -- create a new product object
}
function product_getId( )
{
return this.id;
}
This way, although you will have several instances of the product class, they all point to the instance of the function.
Could try doing something like this (without jquery)
Basic prototypal object:
function Product(id){
this.id = id;
}
Product.prototype.getId() = function(){return this.id;};
var Products = [];
Products[0] = new Product(7);
Products[1] = new Product(8);
Products[2] = new Product(9);
alert(Products[2].getId());
IMO I found a pretty good answer right here:
Return String from Cross-domain AJAX Request
...I could serialize my
data in the service as a JSON string
and then further wrap that in JSONP
format? I guess when it comes over to
the client it would give the JSON
string to the callback function.
That's not a bad idea. I guess I would
also have the option of sending a
non-JSON string which might allow me
to just use eval in the callback
function to create new Person objects.
I'm thinking this would be a more
efficient solution in both speed and
memory usage client-side.

Accessing parameter of one function from another in javascript

var vehiclePage = (function(){
// defined these 3 public variable. to share between zoomNoShowFee & submitVehicle
this.obj;
this.rate;
this.idx;
var setPara = function(o,t,i){
this.obj = o;
this.rate = t;
this.idx = i;
}
return {
zoomNoShowFee : function(o,t,i){
// this is existing function. I need to access o,t,i inside submitVehicle function.
setPara(o,t,i); // wrote this private function to set values
},
submitVehicle : function(){
// here I need to access zommNoShowFee's parameter
alert(this.rate);
}
} // return
})();
vehiclePage.zoomNoShowFee(null,5,3);
vehiclePage.submitVehicle(); // getting undefined
zoomNoShowFee is already existing. Some other fellow developer wrote this. I want to use the values passed into zoomNoShowFee parameters inside submitVehicle.
For that I declared 3 public variables at the top and trying to store the values using setPara private function. so that I can access those public variables inside submitVehicle function.
But getting undefined when calling vehhiclePage.submitVehilce()
Fundamentally, I doing something wrong. But don't know where...
Thanks for any help...
In your use of the module pattern, you're mixing a few things. this.obj, this.rate and this.idx are properties of the wrong this object. In fact, they are properties of the global object, and you can verify this:
vehiclePage.zoomNoShowFee(null,5,3);
alert(rate); // alerts '5'
So, you have to store your values somewhere else. It's quite easy, though : just use regular variables instead of properties and you're good to go :
var vehiclePage = (function(){
var obj, rate, idx;
var setPara = function(o,t,i){
obj = o;
rate = t;
idx = i;
}
return {
zoomNoShowFee : function(o,t,i){
setPara(o,t,i);
},
submitVehicle : function(){
alert(rate);
}
} // return
})();
vehiclePage.zoomNoShowFee(null,5,3);
vehiclePage.submitVehicle();

Categories

Resources