In JavaScript, must bound parameters be in the anonymous function parameters? - javascript

Consider the code below, which works fine. It is the outcome of some debugging. It appears to work because I have inlcluded selectedRowNum not only in bind but it seems I am also required to include selectedRowNum as a parameter to the anonymous callback that is run by .end
Does this make sense? If I bind a variable, must I also include it as a param to the function I am binding it to?
for (var i = selectedRows.length; i--;) {
var selectedRowNum = selectedRows[i];
console.log('outer selectedRowNum');
console.log(selectedRowNum);
var url = urlbase + '/' + this.state.data[selectedRowNum].id;
request
.del(url)
.end(function(selectedRowNum, err, res) {
var data = this.state.data.slice();
data.splice(selectedRowNum, 1);
this.setState({data: data});
this.forceUpdate();
}.bind(this, selectedRowNum));
};

Yes, you need to.
The args values passed to the bind() will be prepended to the called functions param list, so you need to receive it in the target function as arguments.
A simple example will be
function x(p1, p2, p3) {
console.log(p1, p2, p3)
}
var fn = x.bind(window, 1);
fn(2, 3);
fn('a', 'b');
where we are passing an additional param 1 to the bind and when the binded function is called we are passing 2 and 3, now when we receive it in the method we need to have 3 parameters there.

How else do you expect to reference the bound value that you're trying to pass in?
function(selectedRowNum, err, res) { here selectedRowNum is simply a reference to the first argument that's passed in.
Well, technically the answer is no you don't need to. You could not list it as an argument, and refer to it as arguments[0]

Arguments are not absolutely required to be in the anonymous function declaration because you can access arguments via the arguments object as in arguments[0] and arguments[1].
But it is best to declare them as named arguments because it makes your code easier to read and write. If you don't declare them as named arguments in the anonymous declaration, then there is no symbolic name by which to refer to them and you are forced to use the arguments object to access them.
This is an important aspect of Javascript. Declaring named function arguments in a function declaration (whether anonymous or not) just gives you name by which you can refer to that specific argument. Since having a symbolic name is an important part of creating readable code, it is generally considered a good practice to follow.
When you use .bind() with arguments in addition to just the first argument, you are prepending more arguments to the actual function call. This will shift any other arguments later in the argument list. In order to access the correct argument, the anonymous function declaration must use the variables from the right spot in the argument list. If you don't include the extra variables that are added via .bind(), then your other arguments will be out of position and will have the wrong values.

Related

Are function-arguments not necessarily objects?

I'm learning functional programming and node.js, and I came across this odd problem when using Function.prototype.apply and .bind.
function Spy(target, method) {
var obj = {count: 0};
var original = target[method]
target[method] = function (){//no specified arguments
obj.count++
original.apply(this, arguments)//only arguments property passed
}
return obj;
}
module.exports = Spy
This code works, it successfully spies on target.method.
//same code here
target[method] = function (args){//args specified
obj.count++
original.apply(this, args)//and passed here
}
//same code here
This code, however, does not. It gives an error message: TypeError: CreateListFromArrayLike called on non-object.
And then the biggest surprise is, this method works perfectly fine.
//same code here
target[method] = function (args){
obj.count++
original.bind(this, args)
}
//same code here
So why exactly do I get this error? Is it because function arguments are not necessarily objects? Or is it because apply has a stricter description than bind?
In this version:
target[method] = function (args){//args specified
obj.count++
original.apply(this, args)//and passed here
}
Here you are not taking all the arguments but just one, named args. Since apply expects an array like object you cannot use args since it is only the first argument passed to the original target.
You can change it to:
target[method] = function (arg){ //only one argument specified
obj.count++
original.apply(this,[arg]) //one argument passed here
}
Now it works, but you can only spy on one argument functions. Using call would be better since you only have one extra argument:
target[method] = function (arg){ //only one argument specified
obj.count++
original.call(this,arg) //one argument passed here
}
Now bind is a totally different animal. It partial applies functions, thus return functions. Imagine you need to send a callback that takes no arguments but calls a function with some arguments you have when making it. You see code like:
var self = this;
return function() {
self.method(a, b);
}
Well. bind does this for you:
return this.method.bind(this, a, b);
When calling either of these returned functions the same happens. The method method is called with the arguments a and b. So calling bind on a function returns a partial applied version of that function and does not call it like call or apply does.
bind is called the same way as call is, even though they do very different things.
If you really wanted to use bind in this way. You could use the spread operator (ES2015) to expand the arguments 'array' to individual arguments:
original.bind(null, ...args);
That will bind the original function with the array values as individual arguments.

Passing arguments to filter()

I have a function that takes an array and then a number of arguments. In that function I want to use the filter() method on the array, but I want to use arguments[i]. As an example:
function destroyer(arr) {
var test = arr.filter(function(value){
return value != arguments[1];
});
return test;
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
Calling arguments[1] there doesn't give me what I want, because I'm guessing the anonymous function has its own arguments that filter() is using. One solution I thought of was creating an array and copying the arguments to that. But I was wondering if there was a way to pass the arguments to filter? I know filter has a thisArg value, but I'm not sure how to use that to get what I want.
Edit: I have tried googling for a solution, and most of the solutions involve using bind or apply. However, I couldn't find anything directly related to filter() or similar methods like forEach, reduce, map, etc. I've tried using "this" as the thisArg value, but that doesn't seem to do anything. I've tried using bind and apply with it, but I can't seem to get the correct syntax for it.
If you have access to an ES6 environment, you can use "spread parameters" (...) to represent your variable-length argument list. Along with arrow functions, and Array#includes, your code could be quite compact:
function destroyer(arr, ...destroy) {
return arr.filter(value => !destroy.includes(value));
}
I have a working solution (with copying the arguments to an array), but I'm more interested in trying to figure how to pass the arguments to filter() to better understand how filter() and other similar methods work.
You don't need to pass the arguments to filter(). The arguments can simply be made available in the scope of the filter callback. However, arguments is special; it refers to the arguments of the current function. There is no way to refer to the arguments of some other function, such as the outer function. Therefore, there is no alternative but to save the arguments under a different variable name (such as args) in the outer function; then you can refer to that variable from within the callback, because the callback "closes" over that variable.
If you're really intent on "passing" the list of numbers to be destroyed to the inner function, instead of just letting it access them by referring to a variable created in the outer scope, then yes, in theory you could use thisArg:
function destroyer(arr/*, number to destroy*/) {
return arr.filter(do_not_destroy, arguments);
^^^^^^^^^ PASS thisArg
}
Now we can define do_not_destroy as
function do_not_destroy(elt) {
return [].slice.call(this, 1).indexOf(elt) === -1;
^^^^ REFER TO thisArg (arguments)
}
Note two things here. First, we refer to the list of arguments as this, because we passed them to filter using thisArg. Second, we cannot write this.slice..., and instead must write [].slice.call(this... , because this is an arguments object, which is not a "real" array and does not have the slice method.
To make do_not_destroy independent of the fact that its parameter is a special arguments object, with an extra parameter (the array) at the beginning, we could do the conversion to an array within the destroyer function:
function destroyer(arr/*, number to destroy*/) {
return arr.filter(do_not_destroy, [].slice.call(arguments, 1));
}
Now we can define do_not_destroy as
function do_not_destroy(elt) {
return this.indexOf(elt) === -1;
}
But now we are basically back to where we started. Instead of "passing" the list of values to be omitted by merely letting the inner function (callback) refer to a variable set in the outer scope, we are passing them to the callback using the back-door thisArg mechanism. I don't really see the point in this.

How do I pass a method through another method without it being called, and pass a variable object into it?

Sorry for my last question. This is the question with better formatting.
I have a method that I am passing a method through :
method("my_passed_method()")
function method(passed_method){
eval(passed_method.replace('()', + '(' + obj + ')' );
}
But this returns :
SyntaxError: missing ] after element list
I'm assuming this is just some simple JSON syntactical error.
I think you probably want this:
method(my_passed_method)
function method(passed_method){
passed_method(obj);
}
Note that when you're calling method, you're passing in a function reference, not a string, and you're not calling my_passed_method (there are no () after it), you're just referring to it.
Within method, you call the function via the variable passed_method, because that variable contains a function reference.
In JavaScript, functions are first class objects. Variables can refer to them, you can pass them around, etc.
Here's a complete, self-contained example:
// The function we'll pass in
function functionToPass(arg) {
display("functionToPass's arg is: " + arg);
}
// The function we pass it into
function functionReceivingIt(func) {
func("foo");
}
// Do it
functionReceivingIt(functionToPass);
Live copy | source
The name of a method, is at the same time, your reference to that method object. The parentheses, optionally with parameters in between them, make javascript call that method. This means your code can be rewritten to:
method(my_passed_method)
function method(passed_method){
passed_method();
}
So, what's going on here?
When you pass in the name my_passed_method, the javascript engine will look what that name maps to, and find that it maps to a functio object.
Than, inside the function call of method, that object is assigned to the parameter name `passed_method. Than, putting parentheses after this name will make javascript try to execute that object, which is indeed possible because this object is a function, and just like any other type, functions are what we call first class citezens. You can treat the just like any other value by assigning them to variables and passing them around.
In Javascript functions are considered objects. You may pass them as parameters to other functions, as demonstrated below.
function something(x){
alert(x);
}
function pass(func){
pass2(func);
}
function pass2(func){
func("hello");
}
pass(something); //alerts hello
Demo:
http://jsfiddle.net/wBvA2/
eval is evil; or so they say. You don't need to pass the function as a string you can just pass the function itself and then use Function.call or Function.apply to pass the argument (or just call it directly):
method(my_passed_method);
function method(passed_method) {
passed_method(obj);
// OR: passed_method.call(window,obj);
}
The circumstances where eval are necessary are very rare.
Note that your code will evaluate obj as javascript, so the outputs may differ.

JavaScript Binding

thank you if you can help. Source of code http://ejohn.org/apps/learn/#84
1) in line 3 of the program below, where it says return context[name] what does this mean? Im guessing that it means name is bound to the context as a result of the apply function? Is that correct?
2)If my guess in 1 is correct, why does it use the [] brackets? Is that just the syntax. When I look at it, it makes me think array or object?
3) When it says apply(context, arguments) is arguments not the same as name or is arguments both context and name together? to put it another way, in the language of the call bind(Button, "click") is arguments only "click" or is it both button and click?
4) I tried to rewrite line 3 by substituting name for arguments like this
return context[name].apply(context, name);
but it didn`t work anymore, which raises the questions
a)if it is returning name bound to context (i.e. context[name]), why isn`t it sufficient to just have apply(context,name)?
b) if arguments includes both name and context, is the third line of the function essentially
return context[name].apply(context, [context, name]);
c) if my assumption in 4(b) is correct, why would we effectively have to have context passed twice in order to bind name to context? which is to say, I dont understand why line 3 doesnt work if you just write apply(context, name) instead of apply(context,arguments)
function bind(context, name){
return function(){
return context[name].apply(context, arguments);
};
}
var Button = {
click: function(){
this.clicked = true;
}
};
var elem = document.createElement("li");
elem.innerHTML = "Click me!";
elem.onclick = bind(Button, "click");
document.getElementById("results").appendChild(elem);
elem.onclick();
assert( Button.clicked, "The clicked property was correctly set on the object" );
Click me!
It may be helpful to understand the basics of JavaScript objects before diving into the specifics. Any JavaScript property can be accessed with the bracket notation, or the dot notation (if it is a valid identifier). It can be confusing since arrays also use this notation. Say there is an object of cars and their makes,
var cars = { Ford: 2007, Honda: 2010, BMW: 2011 };
Then we can access their keys using the dot notation or the bracket notation
cars.Ford // 2007
cars["Honda"] // 2010
Next, remember that functions are first class citizens in JavaScript. So you could use them as ordinary variables including storing them as object property values, in arrays, etc. Let's replace the years in the previous cars example with actual functions,
var cars = {
Ford: function() { alert("2007"); },
Honda: function() { alert("2010"); },
BMW: function() { alert("2011"); }
};
The keys Ford, Honda, and BMW can still be accessed as in the previous example with the dot or bracket notation, with the only difference that this time a function will be returned instead of the integer year.
cars["BMW"] now returns a function which can be directly invoked as
cars["BMW"](); // or
cars.BMW(); // or
var name = "BMW";
cars[name]();
That's not all. There are still two more ways to execute a function - through apply and call. The difference between apply and call is subtle but you should read up more about them.
Finally, arguments represents an array-like object that contains the arguments passed in to a function. This is best demonstrated by an example,
function whatever() {
console.log(arguments);
}
whatever(1, 2); // [1, 2]
whatever("foo", "bar", 23, [4, 6, 8]); // ["foo", "bar", 23, [4, 6, 8]]
whatever(); // undefined
Without giving any names to the function parameters, we were able to log all the arguments passed to the function. Since it is an array like object, access each argument individually as arguments[0], arguments[1], etc.
And now to answer your questions,
1) in line 3 of the program below, where it says return context[name] what does this mean? Im guessing that it means name is bound to the context as a result of the apply function? Is that correct?
context[name] is similar to the cars['Ford'] example above. It is supposed to give a function which is then invoked by calling apply on it. When that function is called, inside the function this will refer to the object - context.
2) If my guess in 1 is correct, why does it use the [] brackets? Is that just the syntax. When I look at it, it makes me think array or object?
Hopefully this was answered above.
3) When it says apply(context, arguments) is arguments not the same as name or is arguments both context and name together? to put it another way, in the language of the call bind(Button, "click") is arguments only "click" or is it both button and click?
arguments has nothing to do with either context or name. It is simply a list of the arguments/parameters that the function was called with. Hopefully the above description cleared this as well.
4) I tried to rewrite line 3 by substituting name for arguments like this
return context[name].apply(context, name);
but it didn`t work anymore
It didn't work because apply expects the second argument to be an Array, and you passed it a String. Try return context[name].apply(context, [name]); instead.
which raises the questions
a) if it is returning name bound to context (i.e. context[name]), why isn`t it sufficient to just have apply(context,name)?
b) if arguments includes both name and context, is the third line of the function essentially
return context[name].apply(context, [context, name]);
arguments has nothing to do with the context, or name. Hopefully this was cleared up in the above examples.
c) if my assumption in 4(b) is correct, why would we effectively have to have context passed twice in order to bind name to context? which is to say, I dont understand why line 3 doesnt work if you just write apply(context, name) instead of apply(context,arguments)
The above answers already answer this part.
1) context[name] just means the property of the "context" object with that name. In the case of:
bind(Button, "click");
that works out to Button["click"], which is the click() function inside the Button object
2) All objects in Javascript are a collection of properties, which can be accessed by their names. Given the definition:
var Button = {
click: function(){
this.clicked = true;
}
};
both Button.click and Button["click"] would refer to the same thing - the function click() inside the Button object.
3) The arguments keyword refers to an array-like object containing all of the arguments passed to a function. In the example, bind() is returning a newly-created function. The "arguments" referred to in that function are whatever arguments that function gets called with. In this case, it's neither context nor name, it's whatever the onclick mechanism passes to the event handler.
Here's a slightly different way to write the code that sets up the event handler:
var elem = document.createElement("li");
elem.innerHTML = "Click me!";
var boundFunction=bind(Button, "click");
elem.onclick=boundFunction;
document.getElementById("results").appendChild(elem);
Maybe this makes it more clear that when you call bind(), it returns a new function. If you were to call the boundFunction like this:
boundFunction("these", "are", "arguments")
The use of arguments is inside the returned function, so arguments would be ["these", "are", "arguments"] in this case. The arguments that were passed to "bind" are used to construct the function that bind returns, so they're no longer relevant when the bound function gets called.
4) Until you understand the basics of how returning a function from another function works, this'll be pretty confusing. The purpose of apply() is to set the "this" keyword for a particular function invocation. Given the definition of Button, you might expect to be able to do this to set up the event handler:
elem.onclick = Button.click;
This doesn't work correctly, because when the event handling code calls the Button.click function, "this" is set to the global context, rather than to the Button instance. The purpose of the bind() function is to make a function that sets "this" appropriately, then calls the function you originally passed to bind().
I have a half-completed blog entry on this which might be a simpler example:
http://codemines.blogspot.com/2010/01/javascript-by-example-functions-and.html

Advanced parameter usage

//This is the function that will run every time a new item is added or the
//list is sorted.
var showNewOrder = function() {
//This function means we get serialize() to tell us the text of each
//element, instead of its ID, which is the default return.
var serializeFunction = function(el) { return el.get('text'); };
//We pass our custom function to serialize();
var orderTxt = sort.serialize(serializeFunction);
//And then we add that text to our page so everyone can see it.
$('data').set('text', orderTxt.join(' '));
};
full code is at http://demos.mootools.net/Dynamic.Sortables
var serializeFunction = function(*el*) { return el.get('text'); };
var orderTxt = sort.serialize(serializeFunction*(el)*);
compare the codes.
Is el being passed or not? what is going on???
I want to learn advanced parameter usage.
If not declaring functions like function name(parameter1, parameter2, parameter3...).
If not calling functions like name(parameter1, parameter2, parameter3...).
If parameters aren't variables.
If declaring functions like function(parameter1, parameter2, parameter3...).
If calling functions like variable(parameter1, parameter2, parameter3...).
If parameters are objects.
I'm interested.
You probably have a bookmark with the lessons in which I'm interested... please, share!!!
The value assigned to "serializeFunction" is actually an anonymous function, you can see it like a pointer or reference to a function, "el" is simply a declared input parameter that will be used then that function will be called.
Looking at the original code of the one that was posted, the call of the sort.serialize function, receives only the function as a parameter, the "serializeFunction" is not being invocated, it's only passed as an argument.
So, the serialize function that receives the reference of the function passed as a parameter it will be in charge of execute it internally.
This is a lambda expression like.
sort.serialize()
accept the function as parameter, not the value.
The first code is probably correct.
In JavaScript, functions are stored in variables just as any other value (as you see with serializeFunction), and sort.serialize only takes a reference to serializeFunction. Then serializeFunction is called from sort.serialize with the current element (el).
The second code would send an undefined value to the serializeFunction (since el has not been defined in that scope) which would throw an error. Even if el was defined, sort.serialize expects a reference to a function, not a value.

Categories

Resources