In going through some exercises about functional programming, and returning a closure, came across this example, and cannot figure out how it works:
function invoker (NAME, METHOD) {
return function(target) {
var targetMethod = target[NAME];
var args = _.rest(arguments);
return function() { return targetMethod.apply(target, args);});
};
};
The test:
var rev = invoker('reverse', Array.prototype.reverse);
var result= _.map([[1,2,3]], rev);
The output should be:
[[3,2,1]]
but i cannot prove it.
Specific questions:
Why is args:
0,1,2,3
Why is console.log(arguments):
[object Arguments]
How to print out the result, to see [[3,2,1]].
When I print out "result", I get:
[object Object]
After correcting the issues in the code (missing semicolons or too many) there appear to be a few closures that get created throughout the script's execution.
First:
function invoker (NAME, METHOD) {
return function(target) { // In the example this is rev
// Begin Scope1 (closure)
var targetMethod = target[NAME]; // NAME is trapped here in Scope1 (defined outside of this function) when the function is executed!
var args = _.rest(arguments);
return function() { //
// Begin Scope2 (closure)
return targetMethod.apply(target, args); // target and args are trapped here in Scope2 (defined in Scope1's function) when the function is executed!
};
};
}
The invoker does nothing until it is called and the first closure (Scope1's that is) does not get created until invoker is actually invoked:
var rev = invoker('reverse', Array.prototype.reverse);
After rev has been initialized by invoker, it has become a variable that contains a function that accepts a single named argument (called target) that when called will look for a function called NAME (in this case 'reverse') on whatever object target is.
When rev is actually called it also returns a function that, when called, will call the target method (reverse in this case) on target (the array passed in). But rev hasn't been called until the next line is run:
var result= _.map([[1,2,3]], rev);
Here is what is happening here: _.map's purpose is to take a list of items and apply a function to each item in this list. The final result will be a new array with the transformed values in it. The line above passes to _.map a list with exactly one item in it; an array. It also passes to _.map a function to transform each item in that list; in this case rev.
So _.map is called and it calls rev on the only item in the list, our [1,2,3] array. The function rev has the effect of returning yet another function that, when called, will remember NAME, target and args. This function now resides inside the first element of the array variable 'result'.
Now for your questions:
The output should be:
[[3,2,1]]
but i cannot prove it.
In the test code there is no output, only a final variable called result. What I think you are looking for is that somewhere in the end there should be a reversed array. You can verify that the original array has been reversed with the following after the call to initialize result:
alert(result[0]().join());
Why is args:
0,1,2,3
args is really a red herring here; _.map passes 3 arguments to its iterator function. The current value (in this case the array [1,2,3], the key or index (in this case 0) and the original list itself. When _.rest is called on arguments, it slices off the first item leaving us with an array containing 0 and [[1,2,3]]. When reverse is called on this array (args) it ends up returning an array that looks like [1,2,3,0] which is these two items reversed. My tests never showed 0,1,2,3. Why is args a red herring here? Because when reverse is called reverse doesn't take any arguments and so args is ignored. If this was a different function on a different object you would probably encounter issues because of the call to _.rest.
Why is console.log(arguments):
[object Arguments]
Because you are basically calling toString() which will print the object's type.
How to print out the result, to see [[3,2,1]]. When I print out
"result", I get:
[object Object]
You can use:
console.log(result[0]().join());
UPDATE: Here is the jsbin link with working code: http://jsbin.com/aYECIDo/5/edit?html,css,js,output
Use console.log and a modern browser with actual development tools.. like chrome, or firefox+firebug. Then you should be able to see in the console the result. Or, when you print it out in HTML, serialize it using JSON.stringify. Example: document.write(JSON.stringify(result)).
That should write out a JSON representation of what you're seeing.
Regarding the [1,2,3] look at the .apply function in the MDN.
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/apply
Apply takes a first argument of scope, and an array of arguments to cause the function to be called with.
Related
I was breaking down some code I found. I got stuck on a specific issue and managed to break it down into a smaller piece. Just keep in mind that this code is part of a much bigger piece of code.
function foo(string, num) {
console.log("calling foo ---", string, num);
}
This following bit looks like it should work. I pass my two arguments into the array that's passed into apply.
But as you can see by the comment I added. The num parameter is undefined. And the string parameter has the second value in the array I passed in, which was intended to be the for num.
foo.bind.apply(foo, ["fdsf", 432])(); // calling foo --- 432 undefined
But if I add an additional value at the beginning of the array (it could be anything), Then everything works smoothly.
foo.bind.apply(foo, [0, "fdsf", 432])(); // calling foo --- fdsf 432
Can anyone please shed some light on what's happening here? If I call bind or apply separately, the array padding is not needed. It only seems to be needed when combining them.
Why is the additional value at the beginning of the array needed?
Also, here is the original code for reference. It's from one of Kyle Simpsons books.
function asyncify(fn) {
var orig_fn = fn,
intv = setTimeout( function(){
intv = null;
if (fn) fn();
}, 0 );
fn = null;
return function() {
if (intv) {
fn = orig_fn.bind.apply(orig_fn, [this].concat([].slice.call(arguments)));
} else {
orig_fn.apply( this, arguments );
}
};
}
There's a lot more going on here. But inside the if statement about 2/3's though the code, you can see it.
The second argument for apply is the array, with the first value (the padding) being the global object (I'm assuming it's the global object, it's not important, it could be anything). followed by any of the arguments that were passed in.
Some of that extra bit is just turning the arguments array-like object into an array.
The first argument that bind accepts is the this value to be used inside the function. So, if you use
fn.bind.apply(fn, arr)
the first item of the arr becomes the first argument of bind, eg, it's equivalent to:
fn.bind(arr[0], arr[1], arr[2] /* ... */ )
So the first item of the array becomes the this value, the second item of the array becomes the first argument, the third item of the array becomes the second argument, etc.
Here's a live example of how "padding" the array with a value at the start becomes the new this value when the function is called:
const obj = { prop: 'val' };
function foo(string, num) {
console.log("calling foo ---", string, num);
console.log('this:', this);
}
const fn = foo.bind.apply(foo, [obj, 'str', 55]);
fn();
As for the repetitive nature of
foo.bind.apply(foo, ...
It's necessary to repeat the function name when calling apply so that when bind is called, it's called with the right calling context - the this value used when .bind is called is how .bind knows which function to bind. (The first argument passed to .apply, just like .call, is the this value to be used in the function being applied)
Problem and Question
I have a small application that asynchronously gets JSON data from a server, formats it into a table, and allows the user to perform some simple operations on the table (sorting, filtering, etc.).
One of the sorting functions accesses DOM elements in the previously-generated table (definition given below).
var SORT = function(){
var my = {};
// public methods
my.byName = function(sign){
(!sign) ? sign=1 : 1;
var T = $("#resultArea").find("tbody");
var R = T.children("tr");
R.sort( function(a,b){
var an = $(a).attr("data-name");
var bn = $(b).attr("data-name");
return sign * an.localeCompare(bn);
});
R.detach().appendTo(T);
}
...
return my; }();
When specifying it as a callback for an element the user can click on, I have two formulations.
$("#sort-button").click(SORT.byName); (pass function as argument)
OR
$("sort-button").click(function(){SORT.byName();}); (pass anonymous closure that calls the function)
The first option fails, but the second works. Here is how it fails on a test case with 536 rows to be sorted:
Former row 2 (which precedes row 1 in alphabetical order) is moved to position 268.
Former row 269 is moved to position 536.
Former row 536 is moved to position 2.
I have tried and failed to construct a MWE that fails in the same way (will update question once I succeed). Questions are: What went wrong? Why does using the anonymous closure work?
Update
An earlier version of the snippet had been sanitized to remove the parameter sign and the line at the start (which would set sign to 1 if it evaluated to false or was undefined), the idea being that by passing sign=-1 as a parameter, a descending sort could be done. When I removed sign from the definition in the live code, the problem went away.
So I found that the offending line is (!sign) ? sign=1 : 1; When replaced by the more transparent if(sign !== undefined){sign=1;} the problem goes away. I think what it does is that it sets global sign to one on the first pass, then on the second pass, sign is defined so it returns 1, and the function ends (only one pass having been completed).
Then why does this error not crash the sort on the anonymous closure approach?
As you found out, the problem comes from that sign parameter. When you pass a function as the event handler and call SORT.byName() without arguments, you'll get sign=1 and everything is as expected. But when you pass the function directly as the handler, it will get called with the Event object as its argument. Suddenly you have an object in your sign variable, and multiplying that with a number will yield NaN (an invalid result for a comparison function), which completely messes up your sort.
When replaced by the more transparent if(sign !== undefined){sign=1;} the problem goes away. I think what it does is that it sets global sign to one on the first pass…
Nope. There is no global sign variable at all. And I guess you actually wanted if (sign === undefined) sign = 1;. Which wouldn't work as well when passing in events, so you might want to use if (!Number.isFinite(sign)) sign = 1;.
Without seeing more of your code I can't really tell what's going on. The snippet you have posted seems alright. Regarding your question, there is a subtle difference when you pass SORT.byName vs. sending it wrapped in an anonymous function. Specifically, it is the value of this in the byName function when it is executed.
When you do click(SORT.byName), you are sending a direct reference to the function, which means that when it gets called, the value of this is whatever the jQuery handler for click sets it to be when before it calls your callback function; usually this is a reference to the element that fired the event.
However, when you do click(function() { SORT.byName(); }), the value of this in byName is the SORT object (but this in the anonymous function is still whatever jQuery sets it to). This is because here you're invoking the function explicitly as a method of the SORT object.
So if your sort functions rely on the value of this and assume it to be the SORT object, you can run into issues.
Here's some code that demonstrates this behavior:
var obj = {
field: 10,
method: function() {
console.log(this);
}
};
// The first argument to apply sets the value of "this"
function call(f) {
f.apply("not this", []);
}
call(obj.method); //logs "not this"
call(function() { // logs obj
obj.method();
});
Searching through the DOM and sorting is not fun. You would be better off keeping some state, sorting, and then appending the new results to the DOM after you remove the old like so.
var myNumbers = [
[1,'one'],
[4,'four'],
[2,'two'],
[6,'six'],
[3,'three'],
[8,'eight'],
[7,'seven'],
[5,'five'],
[10,'ten'],
[9,'nine']
];
myNumbers.sort(function(a,b){
if (a[0] > b[0]) {
return 1;
}
if (a[0] > b[0]) {
return -1;
}
return 0;
});
var T = $("tbody");
var R = T.children("tr");
R.detach()
After that you can prepend your results in a loop like you addElemnt function but a loop instead.
I am writing a Javascript function to count the number of instances of an element in an unsorted array. It has a method signature like this
Array.prototype.numberOfOccurrences = function() {
}
Here is an example of expected behavior
var arr = [4, 0, 4];
Test.assertEquals(arr.numberOfOccurrences(4), 2);
My problem is that I don't know how to access the elements in the array. The function doesn't take any parameters so how do I reference the array being passed in?
Note: The instructions aren't very descriptive for this kata on code wars and adding a parameter to the function returns some error unexpected token.
Inside the function you are creating into the Array.prototype you can access all the prototype functions through the "this" keyword.
Meaning you can access the array items using numeric properties like this[0] or this[1] to a access the first and second item respectively.
You can also call functions which allows you to iterate over each item on the array, such as: forEach, filter, etc.
Please refer this page to see everything you can do with the array prototype:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/prototype
Lastly don't forget that the JavaScript implementation varies on each browser, so a function that works on Chrome, might not work on InternetExplorer, always confirm on caniuse.com If the function you are used has the same implementation on your targets browsers.
Cheers.
Whether you should extend javascript base objects aside, this is your friend:
Array.prototype.numberOfOccurrences = function(valueToFind) {
return this.filter(function(item) {
return item === valueToFind;
}).length;
}
var a = [1,2,3,3,3,3];
console.log(a.numberOfOccurrences(3)); //4
As noted above, if you're not able to change the function signature for whatever reason you can specify it as follows:
Array.prototype.numberOfOccurrences = function() {
var valueToFind = arguments[0];
...
}
I would recommend adding the parameter to the function for clarities sake. Seems counter intuitive for a function like numberOfOccurences to not take in a parameter - numberOfOccurences of what?
Fiddle: http://jsfiddle.net/KyleMuir/g82b3f98/
You might try using the locally available variable 'arguments' inside of the function. So for example, your code might look like thsi:
Array.prototype.numberOfOccurrences = function() {
var args = arguments || {};
var testArray, testCheck;
if (args[0] && Array.isArray(args[0]) {
// do something with the array that was the first argument, like:
testArray = args[0];
testCheck = testArray.indexOf(args[1]);
return testCheck;
} else {
// do what you want to do if the function doesn't receive any arguments or the first argument
// received isn't an array.
}
}
'arguments' is always available to you inside a declared function.
i have a function in javascript where i need to retrieve the last element of the list. the list can be an array, string, list in numbers (not array). I tried converting the list into a String and then an array and retrieving it by index, but that's not working.
Here is the code I tried:
function last(list){
var array = new String(list);
array = array.split("");
return array[array.length-1];
}
I don't understand what the problem is because test suite says Expected: 5 instead got: 5
I am using code wars and did not write the tests. Is it expecting a Number and getting a String '5' ? I don't understand types in loosely typed languages very well yet.
from the comments, I think you mean you want to either return the last element in an array, the last character in a string, or the last argument passed if multiple arguments were passed. This would do it:
function last() {
if (arguments.length > 1) { // first we handle the case of multiple arguments
return Array.prototype.pop.call(arguments);
}
value = arguments[0]
if (typeof value === 'string') { // next, let's handle strings
return value.split('').pop();
}
if (Object.prototype.toString.call( [] ) === '[object Array]') {// Arrays are of type object in js, so we need to do a weird check
return value.pop();
}
}
arguments is a pseudo-array that contains all arguments passed into the function, so for last(1,2,3,4,5), arguments would be roughly [1,2,3,4,5]. It's not exactly that though, because arguments has all args in order and a length property, but it's prototype isn't Array, so it isn't truly [1,2,3,4,5] and lacks all of array's functions. This is why we need to call pop in the context of arguments (in javascript, Function.prototype.call calls the function passing the first arguments as the value of this, and all the rest of the arguments into the arguments pseudo-array, so for example last.call([], 1, 2, 3) would call last in the context of a new array and with arguments roughly equal to [1,2,3]).
the rest of the code is pretty straightforward, except for the check to see if value is an array, which is further explained here.
Finally, pop is an array method that removes the last element from an array and returns it.
Hi can someone explain why in backbone example app (http://backbonejs.org/examples/todos/index.html) in remaining() function, is called using apply (this.without.apply(this, this.done());) and not this.without(this.done())
// Filter down the list of all todo items that are finished.
done: function() {
return this.where({done: true});
},
// Filter down the list to only todo items that are still not finished.
remaining: function() {
return this.without.apply(this, this.done());
},
Thank You !
#Update
Debugger output
this.without(this.done())
[child, child, child, child]
this.without.apply(this, this.done());
[child, child, child]
Variable list of arguments
The key is in the way without is written:
function () {
var args = slice.call(arguments);
args.unshift(this.models);
return _[method].apply(_, args);
}
It's anticipating a variable list of arguments, and one way to do that is to use apply:
...
return this.without.apply(this, ['pass', 'these', 'arguments']);
There's more about apply in the MDN documentation.
You asked what is the difference between these two calls:
this.without( this.done() )
vs.
this.without.apply( this, this.done() );
To clarify, let's remove the nested this.done() call. Now the first one is:
var value = this.done();
this.without( value );
That code obviously calls this.without() and passes it a single argument, whatever value was returned by this.done(). If value happens to be an array, the entire array is passed as a single argument.
The second version becomes:
var array = this.done();
this.without.apply( this, array );
That calls this.without() with a variable number of arguments, one argument for each element of array. (And I called it array instead of value this time, because for this code to make sense it has to be an array.)
.apply() also sets this in the called function, so passing this as the first argument just passes this along to that function in the same manner as a regular this.without() method call.
apply also lets you specify the "this" object for the function.
That matters sometimes, like when it is being called in a closure, where "this" might be different than you intended.