I'm having an issue with how my arguments are being passed to a new recursion level. Here's a simplified version of what I'm doing:
var newFunction = function(obj) {
var result = "";
var args = [];
Array.prototype.push.apply(args, arguments);
var thisArg = args.shift()
//do stuff to add onto result with thisArg. This block works, so I'm skipping.
if (args.length !== 0) {
result += newFunction(args);
};
return result;
};
The issue I'm having is related to how 'args' is getting passed into newFunction to cycle back through. When the recursive callback is made, args is passed into the new function scope as a single array argument:
original arguments = ("string", true, 9, "string 2")
new arguments in recursion = ([true, 9, string 2])
It NEEDS to be:
original arguments = ("string", true, 9, "string 2")
new arguments in recursion = (true, 9, "string 2")
I'm pretty sure it's related to how I'm using .apply for the args variable. I'm just not sure how to get around that, since you can't .shift() the 'arguments' object. The way I'm doing it is setting args to be an array; so when it gets passed in, it is passing it as a single array. This is the problem with my code...
Do you see what I'm doing wrong? Thanks in advance.
You can use .apply():
result += newFunction.apply(undefined, args);
The .apply() function is like .call, but it expands the array elements out so that (effectively) the array is copied, element by element, into the arguments object in the newly-called function.
In ECMAScript 5, using apply:
var newFunction = function(thisArg) {
var result = "",
args = [].slice.call(arguments, 1);
// ...
if (args.length) result += newFunction.apply(void 0, args);
return result;
};
In ECMAScript 6, using rest parameters and the spread operator,
var newFunction = function(thisArg, ...args) {
var result = "";
// ...
if (args.length) result += newFunction(...args);
return result;
};
Related
Can I call a function with array of arguments in a convenient way in JavaScript?
Example:
var fn = function() {
console.log(arguments);
}
var args = [1,2,3];
fn(args);
I need arguments to be [1,2,3], just like my array.
Since the introduction of ES6, you can sue the spread syntax in the function call:
const args = [1,2,3];
fn(...args);
function fn() {
console.log(arguments);
}
Before ES6, you needed to use apply.
var args = [1,2,3];
fn.apply(null, args);
function fn() {
console.log(arguments);
}
Both will produce the equivalent function call:
fn(1,2,3);
Notice that I used null as the first argument of the apply example, which will set the this keyword to the global object (window) inside fn or undefined under strict mode.
Also, you should know that the arguments object is not an array, it's an array-like object, that contains numeric indexes corresponding to the arguments that were used to call your function, a length property that gives you the number of arguments used.
In ES6, if you want to access a variable number of arguments as an array, you can also use the rest syntax in the function parameter list:
function fn(...args) {
args.forEach(arg => console.log(arg))
}
fn(1,2,3)
Before ES6, if you wanted to make an array from your arguments object, you commonly used the Array.prototype.slice method.
function fn() {
var args = Array.prototype.slice.call(arguments);
console.log(args);
}
fn(1,2,3);
Edit: In response to your comment, yes, you could use the shift method and set its returned value as the context (the this keyword) on your function:
fn.apply(args.shift(), args);
But remember that shift will remove the first element from the original array, and your function will be called without that first argument.
If you still need to call your function with all your other arguments, you can:
fn.apply(args[0], args);
And if you don't want to change the context, you could extract the first argument inside your function:
function fn(firstArg, ...args) {
console.log(args, firstArg);
}
fn(1, 2, 3, 4)
In ES5, that would be a little more verbose.
function fn() {
var args = Array.prototype.slice.call(arguments),
firstArg = args.shift();
console.log(args, firstArg);
}
fn(1, 2, 3, 4);
In ECMAScript 6, you can use spread syntax (...) for that purpose. It's way simpler and easier to understand than Function.prototype.apply().
Code example:
const fn = function() {
console.log(arguments);
}
const args = [1,2,3];
fn(...args);
How to understand the currying function?
How the newSum and newFind works?
var currying = function(fn) {
var args = [];
return function() {
if (!!arguments.length) {
[].push.apply(args, arguments); // What's the meaning of this writing?
return arguments.callee;
} else {
return fn.apply(this, args);
}
}
}
// accumulation currying
var sum = (function(num){
var ret = 0;
return function(){
for(var i = 0, len = arguments.length; i < len; i++) {
ret += arguments[i];
}
return ret;
}
})();
var newSum = currying(sum);
newSum(1)(2)(3)(4)() // 10
// element find currying
var find = function(arr, el){
return arr.indexOf(el) !== -1;
}
var newFind = currying(find)([1,2,3]);
newFind(1);
newFind(2);
The currying function, and gets a function as an argument, and returns a new function, that when invoked:
If arguments are provided, they are accumulated in the args array
If arguments are not provided, the original function is called with all accumulated arguments.
So, if we look at this call for example: newSum(1)(2)(3)(4)() - there are 5 function invocations:
Calling newSum with 1. Getting the curried function with 1 accumulated.
Calling the curried function with 2 - getting the same function with 1 and 2 accumulated, and so on for 3 and 4.
Calling the curried function without arguments - applying all the accumulated arguments (1, 2, 3, 4) to the original sum function - and getting the correct value, 10.
About [].push.apply(args, arguments); and fn.apply(this, args);: apply is a method on Function.prototype that basically lets you call a function, supplying a context object and an array of arguments. So basically [].push.apply(...) is a trick of concatenating an array into another array, in our case, concat arguments (which is the list of arguments provided to a function upon invocation) into args (the accumulating list of arguments). fn.apply(this, args); is simply calling the original function with all the accumulated arguments. You can read more about it in https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
By the way, newFind(1); and newFind(2); both return a function, that will look for the index of the element only when invoked, meaning newFind(1)() === true
I have two JavaScript objects.
Obj1 - static class
Obj2 - instance
After adding item in Obj1 and running the method isAdded() from Obj2 there is a problem.
obj1.func stores a function, that holds the keyword this for Obj2. If I call Obj1.func([args]) this is now for Obj1 instead of Obj2.
Any answer Please?
var Obj1=function(){};
Obj1.func=null;
Obj1.addItem=function(vstup){
// code for add - AJAX async ....
// after adding
Obj1.func(id, nazev);
};
// ----------------------------
var Obj2=function(){
this.variable=null;
this.promenna2=null;
this.isAdded=function(){
this.variable="added";
alert("ok");
};
};
// ---------------------
// in body
window.onload=function(){
var instanceObj2=new Obj2();
obj1.func=instanceObj2.isAdded();
obj1.addItem("test");
}
You are doing obj1.func = instanceObj2.isAdded() which means: setobj1.functo the result ofinstanceObj2.isAdded(), which is: obj1.func = undefined since obj2.isAdded() returns nothing.
if you then execute obj1.isAdded(), which runs Obj1.func, you are essentially executing undefined as a function.
To fix it:
obj1.func = function() { instanceObj2.isAdded(); };
Calling something within another context (aka: running something and setting "this")
To run something with a different this value:
To set the context of a function, you can use either apply or call
function add()
{
var result = this;
for(var i = 0, l = arguments.length; i < l; i++)
result += arguments[i];
return result;
}
var value = 2;
newValue = add.apply(value, [3,4,5]); // = 2 + 3 + 4 + 5;
// newValue = 5
newValue = add.call(value, 3, 4, 5) // same as add.apply, except apply takes an array.
Creating a new function, with a context
In new browsers (ie9+) it's possible to use Function.prototype.bind to create a callback with a set context (this) and set arguments which precede other arguments.
callback = func.bind(object);
callback = function() { func.apply(object, arguments); }
callback = func.bind(object, 1, 2);
callback = function() { func.apply(object, [1, 2]); };
In javascript this refers to currently being used element, So it's reference keep changing
Best method is to store (this) in a variable. and use it where you want that.
Like
var obj1This=this;
var obj2This=this;
And later use them
obj2This.isAdded();
I wanted to have an optional boolean parameter to a function call:
function test() {
if (typeof(arguments[0]) === 'boolean') {
// do some stuff
}
// rest of function
}
I want the rest of the function to only see the arguments array without the optional boolean parameter. First thing I realized is the arguments array isn't an array! It seems to be a standard Object with properties of 0, 1, 2, etc. So I couldn't do:
function test() {
if (typeof(arguments[0]) === 'boolean') {
var optionalParameter = arguments.shift();
I get an error that shift() doesn't exist. So is there an easy way to remove an argument from the beginning of an arguments object?
arguments is not an array, it is an array like object. You can call the array function in arguments by accessing the Array.prototype and then invoke it by passing the argument as its execution context using .apply()
Try
var optionalParameter = Array.prototype.shift.apply(arguments);
Demo
function test() {
var optionalParameter;
if (typeof (arguments[0]) === 'boolean') {
optionalParameter = Array.prototype.shift.apply(arguments);
}
console.log(optionalParameter, arguments)
}
test(1, 2, 3);
test(false, 1, 2, 3);
another version I've seen in some places is
var optionalParameter = [].shift.apply(arguments);
Demo
function test() {
var optionalParameter;
if (typeof (arguments[0]) === 'boolean') {
optionalParameter = [].shift.apply(arguments);
}
console.log(optionalParameter, arguments)
}
test(1, 2, 3);
test(false, 1, 2, 3);
As Arun pointed out arguments is not an array
You will have to convert in into an array
var optionalParameter = [].shift.apply(arguments);
It's not fancy but the best solution to remove the first argument without side effect (without ending with an additional argument as would do shift) would probably be
for (var i=0;i<arguments.length;i++) arguments[i]=arguments[i+1];
Example :
function f(a, b, c, d) {
for (var i=0;i<arguments.length;i++) arguments[i]=arguments[i+1];
console.log(a,b,c,d);
}
f(1,2,3,4); // logs 2,3,4,undefined
In Jeremy Ashkenas's awesome Underscore.js library, I tried to understand one thing about the source file. I do not understand this:
var slice = Array.prototype.slice;
args = slice.call(arguments, 2);
So that:
args = Array.prototype.slice.call(arguments, 2);
.call or .apply are the methods of the functions. But here, which functions do .call refer to? The first parameter should be the context, but arguments is context? The second parameter should be the params to pass in the functions. Here they are number 2. What does this mean? Sometimes in the library, it uses 1 or 0. Are they the number of the params to pass in the functions?
_.bind = function bind(func, context) {
var bound, args;
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
if (!_.isFunction(func)) throw new TypeError;
args = slice.call(arguments, 2);
return bound = function() {
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
ctor.prototype = func.prototype;
var self = new ctor;
var result = func.apply(self, args.concat(slice.call(arguments)));
if (Object(result) === result) return result;
return self;
};
};
Question 2:
I do not quite understand the logic of this function. Need help to understand. An example should be very helpful.
// Invoke a method (with arguments) on every item in a collection.
_.invoke = function(obj, method) {
var args = slice.call(arguments, 2);
return _.map(obj, function(value) {
return (method.call ? method || value : value[method]).apply(value, args);
});
};
Thank you for help.
The "slice" function on the Array prototype expects that this will refer to the array on which it's supposed to operate. In other words, if you have a real array:
var myArray = [1, 2, 3];
and you call slice():
var sliced = myArray.slice(1);
Then in that call to slice(), this refers to the array "myArray". As Raynos notes in a comment:
myArray.slice(1)
is the same as
myArray.slice.call(myArray, 1);
Thus when you use call() to invoke the function, and pass it arguments as the context object, the slice() code operates on arguments. The other parameters passed via .call() are simply the parameter or parameters for slice() itself. In my example above, note that I passed 1 to the function.
Now as to your second question, that .invoke() function first isolates the arguments passed in after the first two. That means that when you use _.invoke() you pass it two or more arguments: the first is the list to operate on, the second is the method, and the (optional) subsequent arguments are passed to the method for each element of the list.
That call to _.map() is complicated (and in fact I think it's got a little nonsense in it). What it's doing is iterating over the list, calling a function for each value in the list. What that function does to first determine whether the "method" parameter really is a function. If it is, then it calls that function via .apply() with the element of the list as the context. If "method" is not a function, then it assumes it's the name of a property of each list element, and that the properties are functions.
So for example, with a simple list it's pretty simple:
var myList = [1, 2, 3];
var result = _.invoke(myList, function(n) { return this * n; }, 2);
That will give the result [2, 4, 6] because the function I passed multiplies its context object (this) by the parameter passed, and I passed 2 in the call to _.invoke().
With a more complicated list, I can use the second flavor of _.invoke() and call a method on each object in the list:
var getName = function(prefix) { return prefix + " " + this.name; };
var list = [
{ name: "Bob", getName: getName },
{ name: "Sam", getName: getName },
{ name: "Lou", getName: getName }
];
var result = _.invoke(list, "getName", "Congressman");
That will call the "getName" function on each object in the list and return a list made from the results. The effect will be the list ["Congressman Bob", "Congressman Sam", "Congressman Lou"].
Now about that nonsense. In the code for _.invoke():
return _.map(obj, function(value) {
return (method.call ? method || value : value[method]).apply(value, args);
});
That subexpresion method || value will always return the value of "method", or at least almost always barring some exotic trick. If method.call is truthy, then a reference to method must also be truthy. Also, if it were my code, I'd inspect method outside the _.map() callback so that the decision doesn't have to be made over and over again. Maybe something like:
return _.map(obj, method.call ?
function(value) { method.apply(value, args); } :
function(value) { value[method].apply(value, args); }
);