The only reason you are able to call Function.prototype.snapshotFunction
as Function.snapshotFunction
is because Function
is itself a function, as are Object
, String
, Number
, and any other JavaScript "class". If you replaced Function
with String
, this would no longer work. You added a method to all functions and called it on the function instance Function
. So if you tried using this
, it would reference the built-in Function
function. So yes, this should either be a free-standing function, or, if you wanted to make it a method (e.g. getPrimitive.snapshot(["testPrimitive", testPrimitive])
), then remove the first parameter of the function, and replace all references to it in the function with this
. Speaking of this
, I can't think of all the use cases for your function involving this
, but with code like this, it's pretty likely this
will cause something to break. I found an example here.
IMO, the [string, value, string, value, ...] thing is bad API design, and doesn't match the intended semantics of an array/list/collection (then again, neither does using them as tuples, but I'm pretty sure I've seen that before, sigh...). Some alternatives: preferably [{name:theName, value:theValue}, ...]
, but you can also do (because your keys are strings) [{theName:theValue}, ...]
, or even [[theName, theValue], [theName2, theValue2], ...]
(so succumbing to using arrays as tuples) would be better. This is more of an opinion, but I'm sure I'm not alone in thinking this.
Also, out of curiosity, did you want the animals' sounds
property to be able to contain objects as well as arrays? Because, you know for(var k in obj)
iterates through obj
's keys (viewing it as an object), rather than what C#'s foreach does, right?
If you wanted to iterate over an arbitrary object's keys, then you can do what Scott suggested, although I prefer a more inline approach:
for(var s in animal.sounds) {
(function(sound) {
myAnimal["say" + animal.sounds[sound]] = function() {
console.log(animal.sounds[sound]);
}
})(s);
}
Another way to write it (what I would've done at first, although it's basically exactly the same as the above as far as I can see, the above might be better practice though, not sure about this myself):
for(var s in animal.sounds) {
(function() {
var sound = s;
myAnimal["say" + animal.sounds[sound]] = function() {
console.log(animal.sounds[sound]);
}
})();
}
In this case, you could've even done:
for(var s in animal.sounds) {
(function(soundVal) {
myAnimal["say" + soundVal] = function() {
console.log(soundVal);
}
})(animal.sounds[s]);
}
If you wanted to iterate over an array, then it's easier:
// since forEach takes a function, which creates a new scope, you don't have to do it in addition to for's code block (which does not create a new scope in JS)
animal.sounds.forEach(function(soundVal) {
myAnimal["say" + soundVal] = function() {
console.log(soundVal);
}
});
You can also use forEach
with an object and its keys using Object.keys(obj)
(it's a bit different than what for...in iterates through though).
So that takes care of the iteration example. In the demo code you gave with your function, you can do the same thing (in JS parameters are pass reference by value, and same with closures as far as I know, so object mutation is externally visible, while variable reassignment is not, which makes your last example work also).
Yeah, the general consensus seems to be that callbacks are a more elegant way of getting the same behavior, which I do agree with - it's basically the same thing as what I'm doing with less overhead. My code just uses eval to create a new variable/function in an isolated scope and return that function, rather than doing that inline.
I suppose one advantage of this is that I can go back to existing functions and update the variables they're referencing:
But that's reaching pretty far, and is more than offest by some of the problems with using this implementation, like not having access to variables that aren't deliberately passed in (attaching the code to
Function
isolates it from any other variables that would otherwise be farther up the scope chain). Practically, inline callbacks should handle pretty much any scenario where you'd want to use something like this.I'm less concerned with passing in standard arrays for the data - it was something I thought about when writing the code, but for the purposes of a jsfiddle, making an array of objects to handle that value pair or using
arguments
with objects to eliminate the array entirely just increases the amount of code and makes it more cumbersome to use the function. In a production environment, I don't advise using multitype arrays, but in a tech demo, javascript does have multitype arrays, and I consider them to be useful. But that's just a personal stylistic opinion, I do realize it tends to rub people the wrong way and I apologize about that.I would though argue that the
this
breakage you link to is actually working as fully intended. The method creates a variable in the current scope and keeps it in that scope for all future calls: so ifsnapQuack()
started referring to a different location forthis
than its actual location when passed in, I would have some problems.That is, as far as I can tell, expected behavior for all callbacks, not just my implementation: http://jsfiddle.net/danShumway/97QJj/3/