I'm wanting to directly update the object referred by a function argument from within the scope of that function. An example:
var thisObj = { "val" : "original value" };
function modFunc(objRef) {
objRef = { "val" : "modified" };
console.log(objRef); // { "val" : "modified" }
}
console.log(thisObj); // { "val" : "original value" };
I understand why this happens, and I realize that I could have used
objRef.val = "modified";
within modFunc to perform the modification. For reasons specific to my project though, I would like to be able to accomplish something like:
function modFunc(objRef) {
objRef.self = { "val" : "modified" }; // Of course this will not work,
// but is there an Object property or method that allows one to access
// the actual memory pointer being referenced by the variable?
}
where I can specify that I want to directly modify thisObj to reference the new object that I instantiate within modFunc. Is this possible? I know there are tools like Object.assign() that I could use to do this but it's not supported universally, or library tools like bind() that I could use if I imported the library, but it just seems like there might be some native syntax that would allow me to do this and I just haven't been able to find it?..
I've spent some time looking, otherwise I try not to ask questions like this. I also know there are other similar posts to this one, but nothing exactly like what I'm asking and I don't have the 'reputation' here to be able to respond directly to posts etc. so I didn't see an easy way to focus the discussion. Anyway, thanks for any help!
What you're asking for is, essentially, a call-by-reference mechanism, or the ability to explicitly create a reference to a variable. In JavaScript, that's not possible. You can (as you note) pass around references to objects and use those to modify object property values (and to add and remove properties, even), but that's not quite the same thing.
This would be true assignment by reference which javascript doesn't support. It only supports the false, misunderstood kind of assignment by reference.
since you cant deal with references, you can simply make objRef a global variable, and not pass it as an arg at all. then you're updating the original object.
Related
If '_position' is a Point object member of Sprite, with Number members x and y, and its own get/set definitions, how can I apply a higher level 'set' so this will work (EDIT: I would like to reach the 'set' function in Sprite, rather than the 'set' function in Point):
// EDIT: example calling line, embedding context should be unimportant
// 'this' is a Sprite instance
this.position.set(x, y);
Object.defineProperties(Sprite.prototype, {
// a bunch of other properties excised here
position: {
get: function() {
return this._position;
},
set: function(_pos) {
this._position.x = this.transform.x = _pos.x;
this._position.y = this.transform.y = _pos.y;
this.transform.updateTransform();
}
}
};
What happens currently is that "this.position" goes to the "position.get" property, and then "this.position.set" goes through to the Point set function. It seems like attempts to 'set' any object with it's own get/set will drop down to the lowest level.
It feels like there must be a way to change this behaviour, but my google and stackoverflow searches are not coming up with an answer. It's likely there's a proper term for what I want to do here, without which I'm struggling the phrase the question correctly.
For instance:
this is close, but doesn't quite work in my situation
EDIT: I don't control the source file for Point, or the function where the this.position.set(x,y); is held. Although I may be able to get permission for a change to the calling syntax if I can prove that it won't break the other objects which actually want to use the Point set function.
Since Bergi didn't post an answer, I will, since this is a good question.
If you don't control any of the surrounding code then, unfortunately, there are only ugly ways to do it.
A) Return a proxy object that will do whatever you want, as Bergi suggested.
The downside is that the surrounding code may be expecting the object it sent to match the object received. It's not inconceivable that the developer would compare object references instead of values, and then such a comparison would fail. But that would happen in your code already, so if you're using it already and nothing breaks you're good to go.
Also, if you go this way, you should make MyPoint.prototype = new Point() or something so that if new methods are added to Point, they will be available in your class as well.
B) Override this._position.set and/or setters and getters manually for each object in the constructor or wherever you set it. This is slow and extremely dirty and is an idea so bad that I wrote a snippet and then deleted it :)
Here are some options on what you should actually do.
A) Agree with the user of Sprite to use sprite.position = newPos or sprite.setPosition(newPos). Then you should return a Object.freeze()d copy of _position in the getter to make sure it's not used inappropriately.
B) Agree with the author of Point to emit some event when it's changed which could then be listened on by Sprite
You don't understand Object.defineProperties. Don't feel bad, neither did I until I researched it further. You are confusing the set method of an individual property of the Object with a property called set which could really have it's own set method. The value of your property is where you can create a method, if you like. I'm guessing this is what you need to see:
Object.defineProperties(Sprite.prototype, {
set: {
// can also have get and set here
value: function(){
// inside Sprite.prototype.set
}
},
position: {
get: function() {
return this._position;
},
set: function(_pos) {
this._position.x = this.transform.x = _pos.x;
this._position.y = this.transform.y = _pos.y;
this.transform.updateTransform();
}
}
});
I think it's cool. This kind of thing must have been used behind the scene so length properties are really methods.
Say I have the following:
var firebaseARef = new Firebase("http://this.is.my/firebase/url/A/reference")
var firebaseBRef = new Firebase("http://this.is.my/firebase/url/B/reference")
When I define my .on() functions, I'd like to specify a single handler, and then do all of the handling in one place in my code, rather than having to define the functions inline with the .on() definition. To illustrate:
var handleAllFirebaseStuff = function(dataSnapshot){
var name = dataSnapshot.name(); //PROBLEM HERE: returns "reference", no way to distinguish!
switch(name){
case "http://this.is.my/firebase/url/A/reference": //How do I get this full reference from dataSnapshot?
/* do stuff for A reference */
case "http://this.is.my/firebase/url/B/reference": //How do I get this full reference from dataSnapshot?
/* do stuff for B reference */
default:
break;
}
}
firebaseARef.on('value', handleAllFirebaseStuff);
firebaseBRef.on('value', handleAllFirebaseStuff);
The problem is dataSnapshot.name() will only return "reference" in both cases, making it impossible to distinguish between the two references in the switch/case statement!
I'm certain that dataSnapshot contains this information somewhere, but I have yet to uncover it in any convenient fashion. Exploring the dataSnapshotobject in the console, I find that there is an object buried within called path that contains (among other things) an array, using the example above, that would contain ["firebase", "url", "A", "reference"], but there is no easy way to access it.
If I had access to that array, I could rebuild the URL or find a more convenient way to handle the switch/case statement. I think a full string of the reference would be more appropriate as an easily accessible value from dataSnapshot.
To get back from a Snapshot to the full URL, you do:
snapshot.ref().toString()
The toString part is somewhat counter-intuitive. I often find myself having to test it, to see if that's indeed the way to go.
hint It would be nice if there was also a more explicit getUrl method hint
UPDATE:
With recent SDK versions ref is no longer a function, so you'll have to use:
snapshot.ref.toString();
at the moment I'm writing a small app and came to the point, where I thought it would be clever to clone an object, instead of using a reference.
The reason I'm doing this is, because I'm collecting objects in a list. Later I will only work with this list, because it's part of a model. The reference isn't something I need and I want to avoid having references to outside objects in the list, because I don't want someone to build a construct, where the model can be changed from an inconsiderate place in their code. (The integrity of the information in the model is very important.)
Additional I thought I will get a better performance out of it, when I don't use references.
So my overall question still is: When should I prefer a clone over an reference in javascript?
Thanks!
If stability is important, then clone it. If testing shows that this is a bottleneck, consider changing it to a reference. I'd be very surprised if it is a bottleneck though, unless you have a very complicated object which is passed back and forth very frequently (and if you're doing that it's probably an indication of a bad design).
Also remember that you can only do so much to save other developers from their own stupidity. If they really want to break your API, they could just replace your functions with their own by copying the source or modifying it at runtime. If you document that the object must not be changed, a good developer (yes, there are some) will follow that rule.
For what it's worth, I've used both approaches in my own projects. For small structs which don't get passed around much, I've made copies for stability, and for larger data (e.g. 3D vertex data which may be passed around every frame), I don't copy.
Why not just make the objects stored in the list immutable? Instead of storing simple JSON-like objects you would store closures.
Say you have an object with two properties A and B. It looks like that:
myObj = {
"A" : "someValue",
"B" : "someOtherValue"
}
But then, as you said, anyone could alter the state of this object by simply overriding it's properties A or B. Instead of passing such objects in a list to the client, you could pass read-only data created from your actual objects.
First define a function that takes an ordinary object and returns a set of accessors to it:
var readOnlyObj = function(builder) {
return {
getA : function() { return builder.A; },
getB : function() { return builder.B; }
}
}
Then instead of your object myObj give the user readOnlyObj(myObj) so that they can access the properties by methods getA and getB.
This way you avoid the costs of cloning and provide a clear set of valid actions that a user can perform on your objects.
I'm looking for a way to inject properties from "this" into local function scope, so i dont need write 'this.' when referencing to this properties.
Exact details are displayed in this code http://jsfiddle.net/wwVhu/3/, look at this part
...
//it's how it works
doStuff: function(param) { $('#output').html(this.value + param) }
//it's how i want it work - without referencing to this
//doStuff: function(param) { $('#output').html(value + param) }
I know it could be achieved by wrapping function code in "with(this) { ... }", but what are other options?
Writing "with(this)" in the beginning of every method or using js aop is what i'm trying to avoid.
Why would you want to do this? It's namespaced because it makes sence. this references to the element the listener is listening on. And it contains a lot more information than just the value.
If you want the value in another variable, you can do:
var value = this.value
There are basically four options:
You keep it the way it is. Context and local scope are different objects, combining them is bad practice and leads to collisions.
You add the value property as the 2nd parameter to the doStuff function.
You nickname this with a shorter identifier. I often find myself use $t.
You use with(this) $('#output').html(value + param);. This is a bad coding practice, as explained in 1). Your code becomes broken the second there is a param property in this.
I've always passed arguments to a function like so:
setValue('foo','#bar')
function setValue(val,ele){
$(ele).val(val);
};
Forgive the silly example. But recently I have been working on a project that has some functions that take a lot of arguments. So I started passing the arguments through as an object (not sure if that's the correct way to put that), like so:
setValue({
val:'foo',
ele:'#bar'
});
And then in the function:
function setValue(options){
var value = options.val;
var element = options.ele;
$(element).val(value);
};
My question is, is there a better way to do that? Is it common practice (or okay) to call these 'options'? And do you typically need to 'unpack' (for lack of a better term) the options and set local vars inside the function? I have been doing it this way in case one of them was not defined.
I'm really looking to not create bad habits and write a bunch of code that is ugly. Any help is appreciated and + by me. Thanks.
I do the exact same thing, except I don't declare a new variable for each option inside the function.
I think options is a good name for it although I shorten it to opts.
I always have a "default" object within the function that specify default values for each available option, even if its simply null. I use jQuery, so I can just use $.extend to merge the defaults and user-specified options like this: var opts = $.extend({}, defaults, opts);
I believe this is a great pattern. I've heard an options object like this referred to as a "builder object" in other languages (at least in the context of object creation). Here are some of the advantages:
Users of your function don't have to worry about what order the parameters are in. This is especially helpful in cases like yours where the method takes a lot of arguments. It's easy to get those mixed up, and JavaScript will not complain!
It's easy to make certain parameters optional (this comes in handy when writing a plugin or utility).
There are some pitfalls though. Specifically, the user of your function could not specify some of the options and your code would choke (note that this could also happen with a normal JS function: the user still doesn't have to supply the correct arguments). A good way for handling this is to provide default values for parameters that are not required:
var value = options.val || 0;
var element = options.ele || {};
$(element).val(value);
You could also return from the function immediately or throw an exception if the correct arguments aren't supplied.
A good resource for learning how to handle builder objects is to check out the source of things like jQueryUI.
I realize this question is a year old, but I think the cleanest way to pass an arbitrary number of arguments to a JavaScript function is using an array and the built in apply method:
fun.apply(object, [argsArray])
Where fun is the function, object is your scope/context in which you want the function to be executed and the argsArray is an array of the arguments (which can hold any number of arguments to be passed.
The current pitfall right now is that the arguments must be an array (literal or object) and not an array-like object such as {'arg' : 6, 'arg2' : "stuff"}. ECMAScript 5 will let you pass array-like objects, but it only seems to work in FireFox at the moment and not IE9 or Chrome.
If you look at the jQuery implementation, it uses an options class to handle most of the arbitrary-number-of-parameters functions, so I think you are in good company.
The other way is to test for arguments.length, but that only works if your arguments are always in the same order of optionality.
It's worth remembering that all functions have a bonus parameter called arguments that is an object very much like a JS array (it has length but none of the array functions) that contains all the parameters passed in.
Useful if you want to pass in a range of parameters (e.g.
function Sum() {
var i, sum = 0;
for (i=0; i < arguments.length; i++){
sum+=arguments[i];
}
return sum;
};
If this isn't the case and you just have a lot of parameters, use the params object as you've described.
Nothing wrong with that practice.
"Options" seems like as good a name as any.
You don't need to "unpack" them, but if you'll be accessing the same item several times, it will be a little more efficient to reference them in local variables because local variable access is generally quicker than property lookups.