Splat operators in JavaScript, equivalent to *args and **kwargs in Python? - javascript

I use Python a lot, and I am just quickly learning JavaScript right now (or should I say re-learning). So, I wanted to ask, what is the equivalent of *args and **kwargs in JavaScript?

ES6 added a spread operator to JavaScript.
function choose(choice, ...availableChoices) {
return availableChoices[choice];
}
choose(2, "one", "two", "three", "four");
// returns "three"

The closest idiom for *args would be
function func (a, b /*, *args*/) {
var star_args = Array.prototype.slice.call (arguments, func.length);
/* now star_args[0] is the first undeclared argument */
}
taking advantage of the fact that Function.length is the number of arguments given in the function definition.
You could package this up in a little helper routine like
function get_star_args (func, args) {
return Array.prototype.slice.call (args, func.length);
}
and then do
function func (a, b /*, *args*/) {
var star_args = get_star_args (func, arguments);
/* now star_args[0] is the first undeclared argument */
}
If you're in the mood for syntactic sugar, write a function which transforms one function into another one which is called with required and optional arguments, and passes the required arguments along, with any additional optional arguments as an array in final position:
function argsify(fn){
return function(){
var args_in = Array.prototype.slice.call (arguments); //args called with
var required = args_in.slice (0,fn.length-1); //take first n
var optional = args_in.slice (fn.length-1); //take remaining optional
var args_out = required; //args to call with
args_out.push (optional); //with optionals as array
return fn.apply (0, args_out);
};
}
Use this as follows:
// original function
function myfunc (a, b, star_args) {
console.log (a, b, star_args[0]); // will display 1, 2, 3
}
// argsify it
var argsified_myfunc = argsify (myfunc);
// call argsified function
argsified_myfunc (1, 2, 3);
Then again, you could just skip all this mumbo jumbo if you are willing to ask the caller to pass the optional arguments as an array to start with:
myfunc (1, 2, [3]);
There is really no analogous solution for **kwargs, since JS has no keyword arguments. Instead, just ask the caller to pass the optional arguments in as an object:
function myfunc (a, b, starstar_kwargs) {
console.log (a, b, starstar_kwargs.x);
}
myfunc (1, 2, {x:3});
ES6 Update
For completeness, let me add that ES6 solves this problem with the rest parameter feature. See Javascript - '...' meaning

I found a good solution here:
http://readystate4.com/2008/08/17/javascript-argument-unpacking-converting-an-array-into-a-list-of-arguments/
Basically, use function.apply(obj, [args]) instead of function.call. apply takes an array as the 2nd arg and 'splats' it for you.

The nearest equivalent is the arguments pseudo-array.

ECMAScript 6 will have rest parameters which do the same thing as the splat operator.

For those who might be slightly lost about *args and **kwargs magic variables read http://book.pythontips.com/en/latest/args_and_kwargs.html
Summary:
*args and **kwargs are just conventional ways of writing the magic variables. You could just say * and ** or *var and **vars. That said lets talk about the JavaScript equivalent in 2019.
*args in python represents a JavaScript Array e.g. ["one", "two", "three"] to pass that into a python function you would just define the function as def function_name(*args): meaning this function accepts "arrays" or "list if you wish" to call that you would simply use function function_name(["one", "two", "three"]):
same thing in JavaScript can be done by using :
function func(x,y,z){
...
}
let args = ["one", "two", "three"];
func(...args)
**or more dynamically as**
function func(inputs<T>:Array){
for(index in inputs){
console.log(inputs[index]);
}
}
let args = ["one", "two", "three"];
func(args)
Have a look at https://codeburst.io/a-simple-guide-to-destructuring-and-es6-spread-operator-e02212af5831
**kwargs on the other hand represent just an array of key value pairs(objects) that's it. Thus
**kwargs e.g is [{"length": 1, "height": 2}, {"length":3, "height": 4}]
in python to define a function accepting array of objects you just say def function_name(**kwargs): then to call that you can do function_name( [{"length": 1, "height": 2}, {"length":3, "height": 4}]):
similarly in JS
const kwargs = [{"length": 1, "height": 2}, {"length":3, "height": 4}]
function func(obj1, obj2){
...
}
func(...kwargs);
**or more dynamically as:**
const kwargs = [{"length": 1, "height": 2}, {"length":3, "height": 4}]
function func(obj){
for(const [key, value] of Object.entries(obj)){
console.log(key, ": ", value)
}
func(kwargs);

Related

Trying to understand concept of arguments array in Javascript

I know arguments is an array-like object in every function, but I'm not sure why we need it and how we use it? Below is one example that confused me...
So here why we can't simply put var args=[], instead, use slice on arguments?
(im not sure if it is a good example to explain arugments, but im also trying to understand how the apply() works here...)
_.delay = function(func, wait) {
var args = slice.call(arguments, 2);
return setTimeout(function(){
return func.apply(null, args);
}, wait);
};
.apply() allows you to call a function while specifying two different things:
The value of the this pointer inside the function call.
A set of arguments to pass to the function when your source of the arguments is an array.
One will typically use .call() if the number of arguments to pass to the function is known in advance and will typically use .apply() when the number of arguments is not know in advance and thus the arguments are in an array or can be put into an array.
Presumably, slice() in the code example you show is Array.prototype.slice so that code example is grabbing the set of arguments AFTER the first two and putting them into their own array so that they can then be passed to the original function.
The arguments object in Javascript is "array-like". It has a .length property and is zero indexed like an array, but it does not have any array methods on it. So, if you want to use an array method on it like .slice() to copy some elements to a new array, you have to manually specify array methods from the Array prototype rather than directly use .slice() on the arguments object.
So, if you called:
function myFunc(a, b, c, d) {
// four arguments present here
}
_.delay(myFunc, 100, 1, 2, 3, 4);
The inner workings of _.delay() would skip the first two arguments passed to it (because those are for use directly in _.delay() and get just the remaing last four arguments and then pass those to the callback function using .apply().
Here's a working snippet example:
var _ = {};
var slice = Array.prototype.slice;
_.delay = function(func, wait) {
var args = slice.call(arguments, 2);
return setTimeout(function(){
func.apply(null, args);
}, wait);
};
function myFunc() {
log("number of arguments passed to myFunc(): " + arguments.length);
log(JSON.stringify(slice.call(arguments)));
}
_.delay(myFunc, 500, 1, 2, 3, 4)
function log(x) {
var div = document.createElement("div");
div.innerHTML = x;
document.body.appendChild(div);
}
This code will get any arguments after the first 2, and put them in a new array. If you set it to a blank array, you will not have any arguments that may have been passed in addition to the named ones.
Consider the difference between the following 2 snippets:
var slice = Array.prototype.slice;
function test(a, b) {
var args = slice.call(arguments, 2);
console.log(args);
}
test(1, 2, 3, 4);
Console Output:
[3, 4]
function test(a, b) {
var args = [];
console.log(args);
}
test(1, 2, 3, 4);
Console Output:
[]
It is necessary to call slice on the arguments array, because the arguments object is only array-like, and does not include this method.

javascript equivalent of php call_user_func

I've found this topic which I've implemented (see accepted answer):
javascript equivalent of PHP's call_user_func()
However, I am having a problem with multiple parameters. I realize what I was doing was turning my parameters into strings and treating it like 1 parameter, but I don't know how to fix this because I am dynamically creating the parameters.
Meaning, I have defined in my code the following:
var a = new Array();
a[0] = new Array();
a[0][0] = 'alert';
a[0][1] = '\'Hello World\'';
a[1] = new Array();
a[1][0] = 'setTimeout';
a[1][1] = 'alert("goodbye world")';
a[1][2] = '20';
Later, I was calling them like this:
var j = 0;
var len = 0;
var fx = '';
var params = '';
for( i in a ){
params = '';
len = a[i].length;
fx = a[i][0]; // getting the function name
a[i].splice( 0, 1 ); // removing it from array
if( len > 1 ){
params = a[i].join(", "); // trying to turn the parameters into the right format, but this is turning it into strings I think
params = params.replace(/\\'/g,'\''); // bc i was adding slashes with PHP
}
window[fx](params);
}
I don't have to use arrays to do this. I don't understand JS OOP (haven't tried yet), though I am comfortable with PHP OOP, so I don't know if there is a way to do this there.
Any help on passing multiple parameters would be appreciated.
Thanks.
First thing to do: Scrap your entire code, start over. Your approach will not get you anywhere where you'd want to be. (Unfortunately I can't tell you where you'd want to be because I cannot make sense of your example.)
There are three ways to call a function in JavaScript.
function foo() { console.log(arguments); }
// 1. directly
foo(1, 2, 3);
// 2. trough Function.call()
foo.call(this, 1, 2, 3);
// 3. trough Function.apply()
var args = [1, 2, 3];
foo.apply(this, args);
call and apply are similar. They let you decide which object the this keyword will point to inside the function (that's the important bit!).
apply accepts an array of arguments, call accepts individual arguments.
The closest thing to call() is PHP's call_user_func(). The closest thing to apply() is PHP's call_user_func_array().
JavaScript objects share something with PHP arrays: They are key/value pairs.
// an anonymous function assigned to the key "foo"
var obj = {
foo: function () { console.log(arguments); }
};
This means you can access object properties either with the dot notation:
// direct function call
obj.foo(1, 2, 3);
Or through square bracket notation (note that object keys are strings):
var funcName = "foo";
obj[funcName](1, 2, 3);
obj[funcName].call(obj, 1, 2, 3);
obj[funcName].apply(obj, [1, 2, 3]);
Square bracket notation gives you the freedom to choose an object property dynamically. If this property happens to be a function, apply() gives you the freedom to choose function arguments dynamically.
Every top-level function that has not been declared as the property of some object will become the property of the global object. In browsers the global object is window. (So the function foo() in my first code block above really is window.foo.)
Note that this does not work like in PHP. It will point to the object the function has been called on, not the object the function "belongs to". (The concept "belongs to" does not really exist in JavaScript. Things can be modeled that way, but it's only a convention.)
With direct calling (obj.foo(1, 2, 3)), this will point to obj. With call and apply, this will point to whatever object you want to. This is a lot more useful than it sounds at first. Most of the time when you want to call functions dynamically, you will end up using apply.
Check out Function.apply:
function test(a, b) { console.log([a, b]) }
test.apply(null, [1, 2]); // => [ 1, 2 ]
Late to the party, but now with ES6 you can simply do
function FunctionX(a,b,c,d){
return a + b + c + d;
}
let fx = "FunctionX";
let params = [ 1, 10, 100, 200 ];
let answer = window[fx]( ... params);
let answer2 = globalThis[fx]( ... params ); // this is more cross-platform
to unpack your argument array

Is there a JavaScript equivalent for C# 'params'?

I need a method that can have an arbitrary number of parameters. In C# we have the params statement. Do we have anything similar in JavaScript?
There is the arguments collection, which contains all arguments passed to the function.
There is a) no need to specify "optional" arguments in the function signature and b) any function accepts any number of parameters.
function foo() {
console.log(arguments);
}
foo(1,2,3,4); // logs [1, 2, 3, 4]
Likewise, there is no need to supply "required" arguments in a function call:
function foo(a, b, c, d) {
console.log(arguments);
}
foo(1,2); // logs [1, 2]
Any argument named in the signature but not supplied in the function call will be undefined.
Note that arguments behaves like an Array, but technically it isn't one. For example, you can call arguments[0], but you can't call arguments.slice(). What you can do to get around this is using the Array prototype:
Array.prototype.slice.call(arguments, 1, 2);
The so-called rest parameter ... is a new (ES6+) addition to the language and makes working with variadic functions more comfortable. #ArunCM's answer explains it.
I know this thread is too old but I believe something is missing here.
There is Rest parameter (introduced in ECMAScript 6) which will allow us to represent an indefinite number of arguments as an array.
It always returns an array. Which means even in defensive JavaScript land, it’s ok to do things like check .length of rest without guards.
Syntax :
function(a, b, ...theArgs) {
// ...
}
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).
Additional reading : Spread
function f(x, ...y) {
// y is an Array
return x * y.length;
}
console.log("Expected result : 3*2 = 6 & Actual result : " + f(3, "hello", true));
console.log("Expected result : 3*4 = 12 & Actual result : " + f(3, "a", true, "b", 1));
//here we are not passing anything to "y" but its still safe to check .length of "y" because it always return an array.
console.log("Expected result : 3*0 = 0 & Actual result : " + f(3));
Yes. arguments.
function concatStrings () {
var str = '';
for (var i = 0; i < arguments.length; i++) {
str += arguments[i];
}
return str;
}
Be aware that arguments isn't an array, so it doesn't have methods like join or push. It's just an array-like object (with numerical properties and a length property) so it can be iterated through.
JavaScript has arguments object inside functions. It contains of all params passed to the function.
More info
It is some sort of implicit in the special variable "arguments". Use like this:
function something(arg1, arg2) {
for (var i = 0; i < arguments.length; i++) {
var x = arguments[i];
}
}
Then you can call it like something(1, 2, 3, 'a', 'b', 'c')
More examples here: http://www.jtricks.com/javascript_tutorials/varargs.html
Javascript functions can accept any number of parameters by default. You can see them with the arguments variable.
See here.

Is it possible to reflect the arguments of a Javascript function?

Is it possible to get all of the arguments a Javascript function is written to accept? (I know that all Javascript function arguments are "optional")? If not, is it possible to get the number of arguments? For example, in PHP, one could use:
$class = new ReflectionClass('classNameHere');
$methods = $class->getMethods();
foreach ($methods as $method) {
print_r($method->getParameters());
}
... or something like that, I haven't touched PHP in a while so the example above may not be correct.
Thanks in advance!
EDIT: Unfortunately, I have to be able to get the arguments outside of the body of the function... Sorry for the lack of clarification, but thanks for the current answers!
This new version handles fat arrow functions as well...
args = f => f.toString ().replace (/[\r\n\s]+/g, ' ').
match (/(?:function\s*\w*)?\s*(?:\((.*?)\)|([^\s]+))/).
slice (1,3).
join ('').
split (/\s*,\s*/);
function ftest (a,
b,
c) { }
let aftest = (a,
b,
c) => a + b / c;
console.log ( args (ftest), // = ["a", "b", "c"]
args (aftest), // = ["a", "b", "c"]
args (args) // = ["f"]
);
Here is what I think you are looking for :
function ftest (a,
b,
c) { }
var args = ftest.toString ().
replace (/[\r\n\s]+/g, ' ').
match (/function\s*\w*\s*\((.*?)\)/)[1].split (/\s*,\s*/);
args will be an array of the names of the arguments of test i.e. ['a', 'b', 'c']
The value is args will be an array of the parameter names if the ftest is a function.
The array will be empty if ftest has not parameters. The value of args will be null
if ftest fails the regular expression match, i.e it is not a function.
it is possible get all the formal parameter name of a javascript:
var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
var FN_ARG_SPLIT = /,/;
var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
function formalParameterList(fn) {
var fnText,argDecl;
var args=[];
fnText = fn.toString().replace(STRIP_COMMENTS, '');
argDecl = fnText.match(FN_ARGS);
var r = argDecl[1].split(FN_ARG_SPLIT);
for(var a in r){
var arg = r[a];
arg.replace(FN_ARG, function(all, underscore, name){
args.push(name);
});
}
return args;
}
this can be tested this way :
var expect = require('expect.js');
expect( formalParameterList(function() {} )).to.eql([]);
expect( formalParameterList(function () {} )).to.eql([]);
expect( formalParameterList(function /* */ () {} )).to.eql([]);
expect( formalParameterList(function (/* */) {} )).to.eql([]);
expect( formalParameterList(function ( a, b, c ,d /* */, e) {} )).to.eql(['a','b','c','d','e']);
Note:
This technique is use with the $injector of AngularJs and implemented in the annotate function. (see https://github.com/angular/angular.js/blob/master/src/auto/injector.js and the corresponding unit test in https://github.com/angular/angular.js/blob/master/auto/injectorSpec.js )
Suppose your function name is foo
Is it possible to get all of the arguments a Javascript function is
written to accept?
arguments[0] to arguments[foo.length-1]
If not, is it possible to get the number of arguments?
foo.length would work
check only required chars. with func.toString().regex you checked full length.so if function is class with 500 lines of code...
function getParams(func){
var str=func.toString();
var len = str.indexOf("(");
return str.substr(len+1,str.indexOf(")")-len -1).replace(/ /g,"").split(',')
}
HBP's answer is what most people are looking for, but if you're the one defining the function, you can also assign a property to the function object. For example,
a.arguments = ['foo', 'bar', 'baz']
function a(foo, bar, baz) {
// do stuff
}
This is debatably more clear, but you'll have to write your arguments twice.
Now when you say outside the body of the function I can only imagine that you want to know what the names of the parameters are? Because as far as the values go, you already know what arguments you are passing. Other answers have said you can get the length of the function, which is the number of parameters it explicitly declares. Now if you want to know the names outside the function, how about the toString hack?
Consider
function f(oh, hi, there) {
return hi + there / oh;
}
Then
alert(f);
What do you see? RIght, just regex them out! Okay, SORRY to bring this up. Perhaps it is not standard ECMAScript, but it, uh, works in Chrome....
args = f => f.toString ()
.replace( /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,'')
.replace(/(\r\n\t|\n|\r\t)/gm,"")
.trim()
.match (/(?:\w*?\s?function\*?\s?\*?\s*\w*)?\s*(?:\((.*?)\)|([^\s]+))/)
.slice (1,3)
.join ('').replace(/\s/g, '').
split (/\s*,\s*/);
/*Test*/
console.log(args((a,b)=>a+b));
console.log(args(function(c,d){return c+d;}));
console.log(args(async function(a,b,c){/**/}));
console.log(args(function* (a,b,c,d){/**/}));
console.log(args(function name(s1,s2){}));
console.log(args(function name(/*comment 1*/ s3/*comment2*/,s4//
){}));
console.log(args(async function* name(/*comment1*/ s5/*comment2*/,s6){}));
console.log(args(async function * name(/*comment1*/ s7/*comment2*/,s8){}));
console.log(args(async function *name(/*comment1*/ s9/*comment2*/,s10){}));
JavaScript is a dialects of ECMAScript, according to ECMAScript standard, a function is also a object, and when a function is called, function can access arguments object, this arguments is array-like object, it has length property, so you can use arguments.length to traverse all arguments passed to this function.
visit http://interglacial.com/javascript_spec/a-13.html#a-13.2.1 for more details.

Passing an array as a function parameter in JavaScript

I'd like to call a function using an array as parameters:
const x = ['p0', 'p1', 'p2'];
call_me(x[0], x[1], x[2]); // I don't like it
function call_me (param0, param1, param2 ) {
// ...
}
Is there a better way of passing the contents of x into call_me()?
const args = ['p0', 'p1', 'p2'];
call_me.apply(this, args);
See MDN docs for Function.prototype.apply().
If the environment supports ECMAScript 6, you can use a spread argument instead:
call_me(...args);
Why don't you pass the entire array and process it as needed inside the function?
var x = [ 'p0', 'p1', 'p2' ];
call_me(x);
function call_me(params) {
for (i=0; i<params.length; i++) {
alert(params[i])
}
}
In ES6 standard there is a new spread operator ... which does exactly that.
call_me(...x)
It is supported by all major browsers except for IE.
The spread operator can do many other useful things, and the linked documentation does a really good job at showing that.
Assuming that call_me is a global function, so you don't expect this to be set.
var x = ['p0', 'p1', 'p2'];
call_me.apply(null, x);
As #KaptajnKold had answered
var x = [ 'p0', 'p1', 'p2' ];
call_me.apply(this, x);
And you don't need to define every parameters for call_me function either.
You can just use arguments
function call_me () {
// arguments is a array consisting of params.
// arguments[0] == 'p0',
// arguments[1] == 'p1',
// arguments[2] == 'p2'
}
While using spread operator we must note that it must be the last or only parameter passed. Else it will fail.
function callMe(...arr){ //valid arguments
alert(arr);
}
function callMe(name, ...arr){ //valid arguments
alert(arr);
}
function callMe(...arr, name){ //invalid arguments
alert(arr);
}
If you need to pass an array as the starting argument you can do:
function callMe(arr, name){
let newArr = [...arr];
alert(newArr);
}
Function arguments may also be Arrays:
function foo([a,b,c], d){
console.log(a,b,c,d);
}
foo([1,2,3], 4)
of-course one can also use spread:
function foo(a, b, c, d){
console.log(a, b, c, d);
}
foo(...[1, 2, 3], 4)
Note this
function FollowMouse() {
for(var i=0; i< arguments.length; i++) {
arguments[i].style.top = event.clientY+"px";
arguments[i].style.left = event.clientX+"px";
}
};
//---------------------------
html page
<body onmousemove="FollowMouse(d1,d2,d3)">
<p><div id="d1" style="position: absolute;">Follow1</div></p>
<div id="d2" style="position: absolute;"><p>Follow2</p></div>
<div id="d3" style="position: absolute;"><p>Follow3</p></div>
</body>
can call function with any Args
<body onmousemove="FollowMouse(d1,d2)">
or
<body onmousemove="FollowMouse(d1)">
you can use the spread syntax
for example:
function print(...inpu){
console.log(...inpu)
}
var arry = ['p0','p1','p2']
print(...arry)
here is the link: modzilla spread syntax refrence document
you can use spread operator in a more basic form
[].concat(...array)
in the case of functions that return arrays but are expected to pass as arguments
Example:
function expectArguments(...args){
return [].concat(...args);
}
JSON.stringify(expectArguments(1,2,3)) === JSON.stringify(expectArguments([1,2,3]))
The answer was already given, but I just want to give my piece of cake. What you want to achieve is called method borrowing in the context of JS, that when we take a method from an object and call it in the context of another object. It is quite common to take array methods and apply them to arguments. Let me give you an example.
So we have "super" hashing function which takes two numbers as an argument and returns "super safe" hashed string:
function hash() {
return arguments[0]+','+arguments[1];
}
hash(1,2); // "1,2" whoaa
So far so good, but we have little problem with the above approach, it is constrained, only works with two numbers, that is not dynamic, let's make it work with any number and plus you do not have to pass an array (you can if you still insist). Ok, Enough talk, Let's fight!
The natural solution would be to use arr.join method:
function hash() {
return arguments.join();
}
hash(1,2,4,..); // Error: arguments.join is not a function
Oh, man. Unfortunately, that won’t work. Because we are calling hash(arguments) and arguments object is both iterable and array-like, but not a real array. How about the below approach?
function hash() {
return [].join.call(arguments);
}
hash(1,2,3,4); // "1,2,3,4" whoaa
The trick is called method borrowing.
We borrow a join method from a regular array [].join. And use [].join.call to run it in the context of arguments.
Why does it work?
That’s because the internal algorithm of the native method arr.join(glue) is very simple.
Taken from the specification almost “as-is”:
Let glue be the first argument or, if no arguments, then a comma ",".
Let result be an empty string.
Append this[0] to result.
Append glue and this[1].
Append glue and this[2].
…Do so until this.length items are glued.
Return result.
So, technically it takes this and joins this[0], this[1] …etc together. It’s intentionally written in a way that allows any array-like this (not a coincidence, many methods follow this practice). That’s why it also works with this=arguments.
There's a better way using JSON not an Array!
// Call a function with a Json Key / Value Pair:
sendMail({param1: p1, param2: p2});
// Function definition and usage of value pairs:
function sendMail(data){
var parameter1 = data.param1;
var parameter2 = data.param2;
}

Categories

Resources