Pass unknown number of arguments into JavaScript function - javascript

Is there a way to pass an unknown number of arguments like:
var print_names = function(names) {
foreach(name in names) console.log(name); // something like this
}
print_names('foo', 'bar', 'baz');
Also, how do I get the number of arguments passed in?

ES3 (or ES5 or oldschool JavaScript)
You can access the arguments passed to any JavaScript function via the magic arguments object, which behaves similarly to an array. Using arguments your function would look like:
var print_names = function() {
for (var i=0; i<arguments.length; i++) console.log(arguments[i]);
}
It's important to note that arguments is not an array. MDC has some good documentation on it: https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Functions#Using_the_arguments_object
If you want to turn arguments into an array so that you can do things like .slice(), .push() etc, use something like this:
var args = Array.prototype.slice.call(arguments);
ES6 / Typescript
There's a better way! The new rest parameters feature has your back:
var print_names = function(...names) {
for (let i=0; i<names.length; i++) console.log(names[i]);
}

ES6/ES2015
Take advantage of the rest parameter syntax.
function printNames(...names) {
console.log(`number of arguments: ${names.length}`);
for (var name of names) {
console.log(name);
}
}
printNames('foo', 'bar', 'baz');
There are three main differences between rest parameters and the
arguments object:
rest parameters are only the ones that haven't been given a separate name, while the arguments object contains all
arguments passed to the function;
the arguments object is not a real array, while rest parameters are Array instances, meaning methods like sort, map, forEach or pop can be applied on it directly;
the arguments object has additional functionality specific to itself (like the callee property).

var
print_names = function() {
console.log.apply( this, arguments );
};
print_names( 1, 2, 3, 4 );

function print_args() {
for(var i=0; i<arguments.length; i++)
console.log(arguments[i])
}

There is a hidden object passed to every function in JavaScript called arguments.
You would just use arguments.length to get the amount of arguments passed to the function.
To iterate through the arguments, you would use a loop:
for(var i = arguments.length; i--) {
var arg = arguments[i];
}
Note that arguments isn't a real array, so if you needed it as an array you would convert it like this:
var args = Array.prototype.slice.call(arguments);

arguments.length. you can use a for loop on it.
(function () {
for (var a = [], i = arguments.length; i--;) {
a.push(arguments[i]);
};
return a;
})(1, 2, 3, 4, 5, 6, 7, 8)

Much better now for ES6
function Example() {
return {
arguments: (...args) =>{
args.map(a => console.log());
}
}
}
var exmpl = new Example();
exmpl.arguments(1, 2, 3, 'a', 'b', 'c');
I hope this helps

Rest parameters in ES6
const example = (...args) => {
for (arg in args) {
console.log(arg);
}
}
Note: you can pass regular parameters in before the rest params
const example = (arg1, ...args) => {
console.log(arg1);
for (arg in args) {
console.log(arg);
}
}

You can use the spread/rest operator to collect your parameters into an array and then the length of the array will be the number of parameters you passed:
function foo(...names) {
console.log(names);
return names;
}
console.log(foo(1, 2, 3, 4).length);
Using BabelJS I converted the function to oldschool JS:
"use strict";
function foo() {
for (var _len = arguments.length, names = new Array(_len), _key = 0; _key < _len; _key++) {
names[_key] = arguments[_key];
}
console.log(names);
return names;
}

You can create a function using the spread/rest operator and from there on, you achieved your goal. Please take a look at the chunk below.
const print_names = (...args) => args.forEach(x => console.log(x));

let x = function(){
return [].slice.call(arguments);
};
console.log(x('a','b','c','d'));

Here is the best answer using spread operator :)
function sum(...nums)
{
let total =0;
for(num of nums)
{
total+=num;
}
console.log(total)
}
console.log(sum(2,4,5,6,7));

I like to do this:
This will not help if you don't know the number of arguments, but it helps if you don't want to remember the order of them.
/**
* #param params.one A test parameter
* #param params.two Another one
**/
function test(params) {
var one = params.one;
if(typeof(one) == 'undefined') {
throw new Error('params.one is undefined');
}
var two = params.two;
if(typeof(two) == 'undefined') {
throw new Error('params.two is undefined');
}
}

Related

How to use an object passed to a fun as context for inner function?

I am iterating through an array of functions and am suppose to call each function USING THE OBJECT AS THE CONTEXT and I don't know how to do it - tried apply but it doesn't work-I guess because I don't fully understand this, apply etc.
Happy about some help!
function calling(obj,arr){
for (var i=0; i<arr.length;i++){
arr[i].apply(null,obj);
}
return arr;
}
var fnA = [
function () {
this.yes = true;
},
function () {
this.no = false;
}
];
var obj = {};
calling(obj, fnA);
obj.yes; //should return true
Are you sure you read the documentation of Function.prototype.apply() correctly?
thisArg, which sets the execution context, is the first parameter of apply, not the second:
arr[i].apply(obj);
As second parameter, you can optionally pass an array of arguments that the function should be called with.
This is example using recursion approach.
If your array of functions should be immutable you need a For loop instead of recursion (or make a copy of initial array).
var functions = [
function() { this.yes = true; },
function() { this.no = false; }
];
var emptyObject = {};
function delegate(object, methods) {
if (methods.length) {
methods.splice(0, 1)[0].apply(object);
delegate(object, methods);
}
}
delegate(emptyObject, functions);
console.log(emptyObject.yes);
console.log(emptyObject.no);

Get a function from an array, javascript

Got an array with functions. I want to do a function that returns a function from the array with function name given as argument.
var arr = [
function Dog(){},
function Cat(){}
];
var getFunction = function(name){
return // should return the function with matching name
};
var dogFunction = getFunction('Dog'); // returns dog function.
https://jsfiddle.net/zcjd9pyz/
Is this possible?
if you do an associative array, it is possible
var arr = {
'dog' : function Dog(){},
'cat' : function Cat(){}
};
arr['dog']();
Functions have a name property:
var getFunction = function(name){
for (var i=0; i<arr.length; i++) {
if (arr[i].name===name) return arr[i];
}
return // return undefined
};
If you want to have a fast access, you can precompute a map by first iterating:
var map = arr.reduce(function(m,f){ m[f.name]=f; return m}, {});
which allows
var fun = map["Dog"];
Computing the map in code instead of typing it yourself lets you not repeat the name. A DRY code is easier to maintain.
EDIT: I'm not sure functions have a name on IE but I can't test it.
In ES6 you could do it without modifying the array (or in all browsers except Internet Explorer if you replace the arrow function with a normal one and use a polyfill for find:
var getFunction = function(name){
return arr.find( func => name === func.name );
};
Even in ES6 though, I don't see a good reason to do that. I think you should follow Deblaton Jean-Philippe's answer and change the array to an object, mapping the names to the functions.
You can use this sample work around of mine, instead of matching for string you can use it based on function name
https://gist.github.com/freewayz/56bd9db6d4164a42be75
var myArray = [{"name" : "pitaside", "id" : 1}, {"name":"github", "id" : 3}]
filterArrayByType: function (arrayToMatch, fieldType, matcher) {
if(! arrayToMatch instanceof Array){throw ("Not an Array")}
var filterTypeToReturn = arrayToMatch.filter((items) => {
var temp;
if (items[String(fieldType)] === matcher) {
temp = items[String(fieldType)]
}
return temp;
}
);
return filterTypeToReturn;
}
var myMatcher = 'github'
var id3 = filterArrayByType(myArray, 'name', myMatcher)[0].id
//returns 3
You can use Function.prototype.toString(). Unlike name it is supported by most of the modern browsers as well as by Node.js.
var arr = [
function Dog ( ) {},
function Cat ( ) {}
];
var getFunction = function(name){
'use strict';
// could use find but it isn't supported by IE
return arr.filter(function (func) {
return /^function\s+(\w+)/.exec(func.toString())[1] === name;
})[0];
};
console.log(getFunction('Dog'));
console.log(getFunction('Cat'));
console.log(getFunction('Unknown'));

Storing data in an array using Javascript Prototypical inheritance

Doing some javascript prototypical inheritance, I would like to push the arguments in my Grades constructor and do the storage manipulation and push the data inside my this.students array using my storage method, and then use the values as I please within my other methods.
But the problem is that when I console log the constructor, it does what I need it to in terms of pushing the data in the this.students array but each object comes up as undefined.
This is weird because if I run the for loop inside the Grades constructor it will work perfectly. But I would like to have a separate method to do this, inside of within my Grades constructor
Any help in pointing me in the right direction would be great! Thanks!
function Grades(studentGrades) {
if(!Array.isArray(studentGrades)) return true;
this.students = [];
this.studentGrades = arguments.length;
this.numRows = 0;
this.numColumns = 0;
this.init();
}
/*
* Method to initialize functions
*/
Grades.prototype.init = function() {
this.storage();
};
/*
* Method to store a list of grades in an array object
*/
Grades.prototype.storage = function() {
for(var i=0; i < this.studentGrades; i++) {
this.students.push(this.studentGrades[i]);
}
};
/*
* Method to add grades
*/
Grades.prototype.addGrades = function(numRows, numColumns, initial) {
for(this.numRows; this.numRows < this.students.length; this.numRows++ ) {
}
};
/*
* Method to display the students average
*/
Grades.prototype.display = function() {
// body...
};
var inputGrades = new Grades( [89,78,93,78], [83,67,93,98], [93,99,73,88] );
console.log(inputGrades);
I think there are some problems with your code, especially with Grades constructor :
function Grades(studentGrades) {
if(!Array.isArray(studentGrades)) return true;
this.students = [];
this.studentGrades = arguments.length;
this.numRows = 0;
this.numColumns = 0;
this.init();
}
You are using an array as parameter to the function but you are passing thtree parameters (arrays), I think this line:
var inputGrades = new Grades( [89,78,93,78], [83,67,93,98], [93,99,73,88] );
Should be like this:
var inputGrades = new Grades( [[89,78,93,78], [83,67,93,98], [93,99,73,88] ]);
And the following line this.studentGrades = arguments.length; is useless in the constructor and may cause problems in your code, and should be replaced with :
this.studentGrades = arguments;
Or if you pass an array of arrays like I did you can use:
this.studentGrades = studentGrades;
Your problem is inside your storage function, originating from definition.
this.studentGrades is actually defined as the length of the array, not the array itself.
If you do not store the input array or pass it on through init(inputGrades) to storage(inputGrades), then you cannot access the original input from your storage prototype.
Better: change constructor bit to:
this.students = [];
this.studentGrades = studentGrades;
And your function inside storage to:
for(var i=0; i < this.studentGrades.length; i++) {
this.students.push(this.studentGrades[i]);
}
And you should be fine I think.
UPDATE: your original function call has a variable number of arguments.
Simplest way to get to complete answer is to change argument variable to:
var inputGrades = new Grades( [[89,78,93,78], [83,67,93,98], [93,99,73,88]]);
Now you send only one argument, an array of arrays.
Alternative: change the function to
function Grades() { // so no input argument
if(!Array.isArray(studentGrades)) return true;
this.students = [];
this.studentGrades = Array.prototype.slice.call(arguments);
this.numRows = 0;
this.numColumns = 0;
And then you should be able to send in multiple arguments.

Simulating Array Functionality

Good day! I have this code:
function MyArray() {}
MyArray.prototype.length = 0;
(function() {
var methods = ['push', 'pop', 'shift', 'unshift',
'slice', 'splice', 'join'];
for (var i = 0; i < methods.length; i++) (function(name) {
MyArray.prototype[ name ] = function() {
return Array.prototype[ name ].apply(this, arguments);
};
})(methods[i]);
})();
I need explanation. I understood that "methods" is array of real methods, which just "exported" to our new class. But, what is this: MyArray.prototype.length = 0; ? Author create new prototype property and assign it zero. And later use this new property!
var mine = new MyArray();
mine.push(1, 2, 3);
assert(mine.length == 3 ...
.....
How it is work? "length" have not instantiation in code above!
Its getting initialized at zero so that if you never call any of its functions, it will return zero (like a real array) and not undefined. Also it needs to start at zero so that the methods update it correctly. in your example, length its 3 because the push method did so.
You can't really subclass Array http://perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/
So if you create an instance of MyArray you can't do: MyArr[0]=...
You can wrap an array inside MyArray and take advantage of the Array functions:
var MyArray=function() {
this.arr=[];
[].push.apply(this.arr,arguments);
//following doesn't work in older browsers
Object.defineProperty(this,"length",{
get:function(){return this.arr.length;},
enumerable:true,
configurable:true
});
}
MyArray.prototype.valueOf=function(){return this.arr;};
(function() {
var methods = ['push', 'pop', 'shift', 'unshift',
'slice', 'splice', 'join'],i=methods.length
while(--i!==-1){
;(function(name) {
MyArray.prototype[ name ] = function() {
console.log(arguments);
return Array.prototype[ name ].apply(this.arr, arguments);
};
}(methods[i]));
}
}());
var mArr1=new MyArray(1,2,3);
console.log(mArr1.slice(0,1));
//you cannot do this: myArr1[0]=22;

Javascript add extra argument

Let's take a look at this code:
var mainFunction = function() {
altFunction.apply(null, arguments);
}
The arguments that are passed to mainFunction are dynamic -- they can be 4 or 10, doesn't matter. However, I have to pass them through to altFunction AND I have to add an EXTRA argument to the argument list.
I have tried this:
var mainFunction = function() {
var mainArguments = arguments;
mainArguments[mainArguments.length] = 'extra data'; // not +1 since length returns "human" count.
altFunction.apply(null, mainArguments);
}
But that does not seem to work. How can I do this?
Use Array.prototype.push
[].push.call(arguments, "new value");
There's no need to shallow clone the arguments object because it and its .length are mutable.
(function() {
console.log(arguments[arguments.length - 1]); // foo
[].push.call(arguments, "bar");
console.log(arguments[arguments.length - 1]); // bar
})("foo");
From ECMAScript 5, 10.6 Arguments Object
Call the [[DefineOwnProperty]] internal method on obj passing "length", the Property Descriptor {[[Value]]: len, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}, and false as arguments.
So you can see that .length is writeable, so it will update with Array methods.
arguments is not a pure array. You need to make a normal array out of it:
var mainArguments = Array.prototype.slice.call(arguments);
mainArguments.push("extra data");
The arguments object isn't an array; it's like an array, but it's different. You can turn it into an array however:
var mainArguments = [].slice.call(arguments, 0);
Then you can push another value onto the end:
mainArguments.push("whatever");
The arguments "array" isn't an array (it's a design bug in JavaScript, according to Crockford), so you can't do that. You can turn it into an array, though:
var mainFunction = function() {
var mainArguments = Array.prototype.slice.call(arguments);
mainArguments.push('extra data');
altFunction.apply(null, mainArguments);
}
Update 2016: You must convert the arguments to an array before adding the element. In addition to the slice method mentioned in many posts:
var args = Array.prototype.slice.call(arguments);
You can also use the Array.from() method or the spread operator to convert arguments to a real Array:
var args = Array.from(arguments);
or
var args = [...arguments];
The above may not be optimized by your javascript engine, it has been suggested by the MDN the following may be optimized:
var args = (arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments));
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments
var mainFunction = function() {
var args = [].slice.call( arguments ); //Convert to array
args.push( "extra data");
return altFunction.apply( this, args );
}
One liner to add additional argument(s) and return the new array:
[].slice.call(arguments).concat(['new value']));
//
// var
// altFn = function () {},
// mainFn = prefilled( altFn /* ...params */ );
//
// mainFn( /* ...params */ );
//
//
function prefilled ( fn /* ...params */ ) {
return ( function ( args1 ) {
var orfn = this;
return function () {
return orfn.apply( this, args1.concat( cslc( arguments ) ) );
};
} ).call( fn, cslc( arguments, 1 ) );
}
// helper fn
function cslc( args, i, j ) {
return Array.prototype.slice.call( args, i, j );
}
// example
var
f1 = function () { console.log( cslc( arguments ) ); },
F1 = prefilled( f1, 98, 99, 100 );
F1( 'a', 'b', 'c' );
//
// logs: [98, 99, 100, "a", "b", "c"]
//
//
In this case it could be more comfortable to use call() instead of apply():
function first(parameter1, parameter2) {
var parameter3 = "123";
secondFunction.call(
this,
parameter1,
parameter2,
parameter3);
},
var myABC = '12321';
someFunction(result, error, myCallback.bind(this, myABC));
Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
For those who like me was looking for a way to add an argument that is optional and may be not the only omitted one (myFunc = function(reqiured,optional_1,optional_2,targetOptional) with the call like myFunc(justThisOne)), this can be done as follows:
// first we make sure arguments is long enough
// argumentPosition is supposed to be 1,2,3... (4 in the example above)
while(arguments.length < argumentPosition)
[].push.call(arguments,undefined);
// next we assign it
arguments[argumentPosition-1] = arguments[argumentPosition-1] || defaultValue;

Categories

Resources