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
Related
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
For example, I'm writing a function that uses two arrays. How can I define this function to call it as a global function, passing it two arrays or as an Array method, passing it a second array:
func([1, 2, 3], [3, 2, 1]);
[1, 2, 3].func([3, 2, 1]);
Here is my idea:
func = function (...args) { // define global function
if (args.length > 1) {
// function called as global with 2 arrays in arguments
} else {
// function called as Array method, with second array in arguments
}
}
Array.prototype.func = func; // define Array method
Is there a more better way?
Thanks.
You could perhaps create a wrapper function for the prototype:
func = function (a,b) { // define global function
// will always have a,b defined
}
Array.prototype.func = function (b){ func(this,b) }; // define Array method
This way it doesnt make any difference for your function.
(Assuming your function expects exactly 2 arguments)
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;
};
Minimum example:
function test() {
console.log(arguments.join(','));
}
test(1,2,3);
I then get:
TypeError: undefined is not a function
However, when I do the same for an array:
console.log([1,2,3].join(','));
I get
"1,2,3"
As expected.
What's wrong with arugments? It's suppose to be an array:
(function () {
console.log(typeof [] == typeof arguments)
})();
true
Arguments is not an array.
(function(){
console.log(typeof arguments);
})();
// 'object'
It is an array-like structure with a length and numeric properties, but it is not actually an array. If you want to, you may use the array function on it though.
function test() {
console.log(Array.prototype.join.call(arguments, ','));
// OR make a new array from its values.
var args = Array.prototype.slice.call(arguments);
console.log(args.join(','));
}
test(1,2,3);
Note, your example works because array is not a type. typeof [] === 'object' also. You can however check if an object is an array by using
Array.isArray(arguments) // false
Array.isArray([]) // true
The problem is that arguments is NOT a javascript Array per se. It behaves like an array in some ways but no in others.
Why don't you try converting it into an pure javascript array. This can be done the following way:
(function () {
var args = Array.prototype.slice.call(arguments, 0);
console.log(typeof [] === typeof args);
}());
i want to run a function, inside another function dynamically...
let say :
<script>
var x = function(r,s){
r(s);
}
var a = function(item){
alert("aA");
}
var b = function(item){
alert("bB");
}
</script>
is this possible?
i take "r" from function x's argument as a function name, and i want to run it.
if r=a, then it will trigger function a()
and if r=b, function b triggered instead...
how could i do that?
You could simply test r and call each function as needed.
if (r === 'a') {
a(s);
} else if (r === 'b') {
b(s);
}
Or, if you have many functions for this or don't always know them all ahead of time, you can organize them in an Object and use bracket operators to treat r as a key name to access them:
var commands = {
a: function(item){
alert("aA");
},
b: function(item){
alert("bB");
}
};
var x = function (r, s) {
var command = commands[r];
if (typeof command === 'function') {
command(s);
}
};
x('a', 'foo'); // alerts `aA`
Or, even just pass a and b as the arguments themselves with your current definition of x().
x(a, 'foo'); // calls `a` as `r`, passing 'foo' along
By passing the name of the function as the first parameter to x when calling it.
x(a, {}); // alert("aA");
x(b, {}); // alert("bB");
Note that you're passing the reference to the function, not a string. This is because functions in JavaScript are objects and are passed by reference, not by value. So var a = function() {...}; actually means that variable a holds a reference to the function.
you mean like:
var x = function(r,s){
window[r](s);
}
var a = function(item){
alert("aA");
}
var b = function(item){
alert("bB:" + item);
}
x('b', 'test');