Consider this object:
function Cow() {
var self = this;
self.color = "blue";
self.weight=55;
}
Cow.prototype.Speak = function(){
var self=this;
alert('moo. i\'m a ' + self.color + ' cow.');
}
var bessy = new Cow();
I'm running into an issue when I try to do $.ajax(...) and pass in 'bessy' as the data parameter. My intention is for the data properties to be serialized and passed over the wire But it will actually execute Speak() upon the ajax call. This is an overly simple example, but highlights the problem I'm having.
To solve this, I've created a function that accepts an object and conditionally deletes members (such as functions). I then pass a copy of the target object to that function, and use my new simplified copy of the object for the ajax call. This seems really cumbersome. Is this a common problem? How do others deal with this? It seems like overkill to create separate DTO JS objects for this purpose because then, when I add a new property, it would need to be added in two places.
Would appreciate any thoughts.
Thanks...
-Ben
You can do it by easily converting the object into json string.
JSON.stringify(bessy) will let you have json string of the bessy variable.
Send that to the server and convert into object again by using json_decode.
Related
Given the following example ...
$("#some-id").on("custom-event-1", function()
{
this.someData = "test";
}).on("custom-event-2", function()
{
var test = this.someData;
...
});
... I am able to store and pass data between custom-event-1 and custom-event-2, however, is this the correct way of doing this? It works, but I'm not sure if things should be done this way, or if the jQuery data method should be used instead. If this approach is acceptable, where exactly is the data being stored? I understand where the jQuery data method stores the data, but the example above I'm not sure about.
I'd recommend you to use .data(id,value).
DOM elements are like a regular object. Ex: A div is an instance of the prototype HTMLDivElement. Test it via document.createElement('div').constructor.
Doing this.someData = 1 is similar to a = new Date(); a.someData = 1 or a = []; a.someData = 1.
It does work but it's not "clean". There is a performance cost to change the structure of an object. And you assume that the object doesn't have the attribute someData already used for something else.
With .data() you are safe.
I'm struggling a lot with Javascript prototyping; I don't seem to understand when I should be declaring an attribute in the prototype vs instantiation. Particularly, I don't understand, why in my code attached (a basic exercise in building a little Router class to treat a document more like an app than a page), attributes set at instantiation are persisting and thus accumulating in what I intend to be separate objects entirely.
Mostly this is a learning exercise, but I've scaled back some of the original code to help with contextual obfuscation*.
Code is here: http://codepen.io/anon/pen/xGNmWM
Basically, each time you click a link, the data-route attribute on the element should be picked up an event listener, a new Route object should be instantiated (and passed information about the intended route); finally the Router class should actually "launch" the Route, ie. make an ajax request or do some in-document stuff, whatever.
Right now, the Route.url.url attribute should be, in my obviously wrong understanding, created anew each time and then informed by passed data. Somehow, this attribute is persisting and thus accumulating passed information from each click.
I truly don't understand why.
**I have not removed anything that would effect my question; really it could be trimmed back even more but I realize the integrity of the question relies on a reasonable facsimile of the original code.
You have two problems.
By Value vs By Reference
In Javascript primitive types, as numbers, booleans and strings, are passed to another functions or set to another variable by value. That means that its value is copyed (cloned).
The object types, as objects, arrays and functions, are passed to another functions or set to another variable by reference. That means that variables of this type are just references to a content to memory. Then when you set an object to a variable, just its memory address is being copied.
When you pass the "route_data" its reference is copied. Then the Route constructor is working on same variable hold by Router.
If you clone your object before pass it the problem is solved.
...
var route_data = this.route_data[ route_name ];
route_data = $.extend(true, {}, route_data); // Clone object using jQuery
var route = new Route( route_name, route_data, request_obj);
...
Prototype
The Javascript has a prototypal inheritance, that means that each object points to its prototype that is another object.
var obj = { name: 'John' };
console.log(obj.__proto__);
All objects root prototype is Javascript Object by default. As noted by example above.
function Person(name) {
this.name = name;
}
Person.prototype = {
getName: function() {
return this.name;
}
}
var obj = new Person('John');
console(obj.getName());
console(obj.__proto__);
console(obj.__proto__.__proto__);
When you use new a new empty object is created and binded as this to specified function. Additionally its object prototype will point to the object specified on prototype of called function.
On get operations the Javascript engine will search on entire prototype chain until specified field is found.
But on set operations if the specified field does not exist on current object, a new field will be created.
When you define the url field on Route this should be static, because when you try to change it a new field is created.
If you verify your Route instance you will note that you have created duplicated url fields. One on object itself and another on prototype.
I would have really appreciated a minimal code example posted here on SO rather than codepen. It would have saved me some time reading your code (you're not paying me for this after all).
Anyway, this is problematic:
Route.prototype = {
// etc..
url : {url: false, type: "get", defer: false}
// etc..
}
Basically, what you're doing is this:
var global_shared_object = {url: false, type: "get", defer: false};
Route.prototype.url = global_shared_object;
Do you see the problem? Now when you do:
var route1 = new Route();
var route2 = new Route();
Both the .url property of route1 and route2 point to the same object. So modifying route1.url.url will also modify route2.url.url.
It should be noted that route1.url and route2.url are different variables. Or rather different pointers. Modifying route1.url will not modify route2.url. However, the way they've been initialized makes them both point to the same object so modifying that object can be done from either pointer.
The key to making this work is to create a new object for .url every time you create a new object. That can be done either in the constructor or in your case your .init() method:
Route = function (name, route_data, request_obj) {
this.url = {url: false, type: "get", defer: false};
this.init(name, route_data, request_obj);
}
Implied Lesson:
The lesson to take from this is that the object literal syntax is actually an object constructor. Not just a syntax.
// this:
{};
// is the same as this:
new Object();
So every time you see an object literal, in your mind you should be thinking new Object() then see if it makes sense to call it just once or do you need a new instance of it every time.
I don't really understand why this piece of code works the way it does.
I call my service and get the expected data returned. I wish to store the pictures seperately, as I need to modify the array and parse it differently to new scopes. The thing I don't understand is, that as I now have created 2 scopes, product and productPictures - why are these still linked?
If I use Lodash to _.sort() productPictures, then the Images object inside scope.Product will also be altered, however I do not want this happen, and I don't know how to isolate the $scope.productPictures. I tried handling the scopes as simple js variables such as var product; and var productPictures, but as I set their data according to the response from the service, they are still linked.
$api.productpage.query({
id: parseInt($stateParams.productId)
}, function (data) {
$scope.product = data[0];
$scope.productPictures = data[0]['Images'];
});
Am I overlooking something super trivial? Should I rather manage the product images in a seperate service request?
Objects are passed by reference in javascript. It means that in this code
$scope.product = data[0];
$scope.productPictures = data[0]['Images'];
$scope.productPictures points to the same object (I guess this is array in your case) as data[0].Images.
To overcome this issue you need to clone object instead of just reference. You might want to check angular.copy method:
$scope.productPictures = angular.copy(data[0].Images);
You create two variables in the scope not two scope.
Object are passed by reference so if you modify one it modidy the other.
Maybe try to clone the object
edit use angular.copy() instead of jquery.extend()
function myclone ( obj){
return jQuery.extend(true, {}, obj);
}
Right, so I have a number of Backbone models going on, and in one of them I have an object that has a set of keys and values, of which the values are modified by locating the key from a string.
So I started out with code that was built on the below principle, I am quite comfortable with how and why this code will output true:
var boundData = {
text: 'value'
}
var key = 'text';
return (boundData[key] === 'value');
So to set the value of the property, I would do something like:
boundData[key] = 'new value';
Then I decided to translate all of my existing classes to Backbone models. And the problem that I hit, was that I can no longer change my properties with an equals operator, instead I use the set method provided by Backbone for models. This method takes a string as the first parameter, this string identifies the key for the variable that I am trying to change.
this.set("boundData[" + settings.name + "]", new OpenCore.boundData(settings));
This does not seem to work, and neither does this:
this.set("boundData." + settings.name, new OpenCore.boundData(settings));
SOLVED IT. Whilst I was writing out the question, I figured out a way to do it. But thought I would leave it here in case others run into the same problem.
This is a solution, whilst it may not be the best (I'd be interested if someone could get the original way sorted.), but it seems to work.
var boundData = this.get('boundData'); //Create a reference of the object with get().
boundData[settings.name] = new OpenCore.boundData(settings); //Update this reference.
//The change will be reflected in the original instance or you can just:
this.set('boundData', boundData);
Hope this helps someone else out!
This is a solution, whilst it may not be the best (I'd be interested if someone could get the original way sorted.), but it seems to work.
var boundData = this.get('boundData'); //Create a reference of the object with get().
boundData[settings.name] = new OpenCore.boundData(settings); //Update this reference.
//The change will be reflected in the original instance or you can just:
this.set('boundData', boundData);
Hope this helps someone else out!
I am trying to learn JavaScript. After reading this page: What does ':' (colon) do in JavaScript?
I tried to replace
var store = new dojo.data.ItemFileReadStore({
url: "countries.json"
});
with
var store = new dojo.data.ItemFileReadStore();
store.url = "countries.json";
It does not work. Can any one please point out the mistake, or explain the correct use of the Colon operator?.
Thanks.
That's not a fair comparison, although you're almost there.
var store = new dojo.data.ItemFileReadStore({
url: "countries.json"
});
//Creates a new store object, passing an anonymous object in with URL
// property set to "countries.json"
The alternative without the colon operator is:
var props={};
props.url="countries.json"
var store = new dojo.data.ItemFileReadStore(props);
//Does same as above but doesn't use :
Not this isn't the only use of : in JavaScript though, it can also be used in the ternary operator (alert(b==c?'equal':'not equal');) and in labels (for example in case statements)
The first passes url parameter to the so-called constructor or the object, which may do something under the hood with it - for example assign it to other variable or property, for example "url2".
The second assigns url property of that object and you don't know if it will be used.
In first code you are creating a new object and passing it to the function as an argument.
While in second part you are running the function and then, you are setting property of store object.
They are totally different thing, as you are not calling function with argument, so it might not run properly. and you are setting return of function to object. not setting property.
In this case, the object literal in your first example is being used to pass in a set of options to the constructor. Constructing the ItemFileReadStore and then trying to set those options may not be equivalent since it may need them in order to build the object to begin with.
You'd need to do something like this to replace : with =:
var options = {};
options.url = 'countries.json';
var store = new dojo.data.ItemFileReadStore(options);
If the second way isn't working, you're probably not returning an Object with new dojo.data.ItemFileReadStore(); which prevents you from extending it with dot syntax. If you have an Object, adding to it like that will work fine.
Edit: Misread, in one you're passing an argument, in the other you're assigning to the return value, so two different things, I'll leave the above as an FYI.
The dojo.data.ItemFileReadStore object probably requires that the url property be present while the object is being created. If that's not the case, then the object doesn't allow you to set that property manually after you have already initialized the object.
The colon is used in JSON to designate the different between a key and a value, when you pass an object structure ({}).