I am looking at a open source javascript application, specifically an extension for firefox.
I am seeing this syntax in multiple places that I do not know what it means if anyone can shed some light on this.
such as..
return (...args)
or...
console.info(message, ...args.slice(1));
any idea what this '...' does? Is it like getting the third argument in or what? Third argument back? Its hard to try and debug this without being able to understand it.
It will unpack an array (args) into a formal argument list. Amongst other things this allows the members of a rest parameter to be passed as a set of formal arguments to another function.
Here's an example:
var stats = function(...numbers) {
for (var i=0, total = 0, len=numbers.length; i<len; i++) {
total += numbers[i];
}
return {
average: total / arguments.length,
max: Math.max(numbers); //spread array into formal params
}
}
stats(5, 6, 8, 5); //{average: 6, max: 8}
Hope this help you understand "..."!
It's an Ecmascript 6 "rest" parameter. When used as a parameter or argument, it lets you receive or pass an array as individual arguments.
http://ariya.ofilabs.com/2013/03/es6-and-rest-parameter.html
The three periods after the final parameter's type (before parameter's name) indicate that the final argument may be passed as an array or as a sequence of arguments. Varargs can be used only in the final argument position(static)
Related
I recently read some source code that contained a function similar to the one below:
function foo(someArray) {
const arrayPrime = someArray.map(x => x * 2,);
return arrayPrime;
}
To my surprise, the code above is valid - no syntax errors.
Playing with this code more, we can add any number of values separated by commas without generating a syntax error:
function foo(someArray) {
const arrayPrime = someArray.map(x => x * 2, 3, 4, 5, 6);
return arrayPrime;
}
Invoking both versions of foo with foo([11,2,3]) yields 22,4,6.
Can someone explain why this is legal?
Those are additional arguments to the map() method.
map() takes 1 optional argument, the value to be passed as the this context to the callback function. Since arrow functions can't have their this value altered, so it's ignored in this case.
The remaining arguments are ignored completely. JavaScript doesn't report errors when a function is called with more arguments than it needs. (It also doesn't report errors for missing arguments, they're just set to undefined).
Is there a way to allow "unlimited" vars for a function in JavaScript?
Example:
load(var1, var2, var3, var4, var5, etc...)
load(var1)
Sure, just use the arguments object.
function foo() {
for (var i = 0; i < arguments.length; i++) {
console.log(arguments[i]);
}
}
In (most) recent browsers, you can accept variable number of arguments with this syntax:
function my_log(...args) {
// args is an Array
console.log(args);
// You can pass this array as parameters to another function
console.log(...args);
}
Here's a small example:
function foo(x, ...args) {
console.log(x, args, ...args, arguments);
}
foo('a', 'b', 'c', z='d')
=>
a
Array(3) [ "b", "c", "d" ]
b c d
Arguments
0: "a"
1: "b"
2: "c"
3: "d"
length: 4
Documentation and more examples here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters
Another option is to pass in your arguments in a context object.
function load(context)
{
// do whatever with context.name, context.address, etc
}
and use it like this
load({name:'Ken',address:'secret',unused:true})
This has the advantage that you can add as many named arguments as you want, and the function can use them (or not) as it sees fit.
I agree with Ken's answer as being the most dynamic and I like to take it a step further. If it's a function that you call multiple times with different arguments - I use Ken's design but then add default values:
function load(context) {
var defaults = {
parameter1: defaultValue1,
parameter2: defaultValue2,
...
};
var context = extend(defaults, context);
// do stuff
}
This way, if you have many parameters but don't necessarily need to set them with each call to the function, you can simply specify the non-defaults. For the extend method, you can use jQuery's extend method ($.extend()), craft your own or use the following:
function extend() {
for (var i = 1; i < arguments.length; i++)
for (var key in arguments[i])
if (arguments[i].hasOwnProperty(key))
arguments[0][key] = arguments[i][key];
return arguments[0];
}
This will merge the context object with the defaults and fill in any undefined values in your object with the defaults.
It is preferable to use rest parameter syntax as Ramast pointed out.
function (a, b, ...args) {}
I just want to add some nice property of the ...args argument
It is an array, and not an object like arguments. This allows you to apply functions like map or sort directly.
It does not include all parameters but only the one passed from it on. E.g. function (a, b, ...args) in this case args contains
argument 3 to arguments.length
Yes, just like this :
function load()
{
var var0 = arguments[0];
var var1 = arguments[1];
}
load(1,2);
As mentioned already, you can use the arguments object to retrieve a variable number of function parameters.
If you want to call another function with the same arguments, use apply. You can even add or remove arguments by converting arguments to an array. For example, this function inserts some text before logging to console:
log() {
let args = Array.prototype.slice.call(arguments);
args = ['MyObjectName', this.id_].concat(args);
console.log.apply(console, args);
}
Although I generally agree that the named arguments approach is useful and flexible (unless you care about the order, in which case arguments is easiest), I do have concerns about the cost of the mbeasley approach (using defaults and extends). This is an extreme amount of cost to take for pulling default values. First, the defaults are defined inside the function, so they are repopulated on every call. Second, you can easily read out the named values and set the defaults at the same time using ||. There is no need to create and merge yet another new object to get this information.
function load(context) {
var parameter1 = context.parameter1 || defaultValue1,
parameter2 = context.parameter2 || defaultValue2;
// do stuff
}
This is roughly the same amount of code (maybe slightly more), but should be a fraction of the runtime cost.
While #roufamatic did show use of the arguments keyword and #Ken showed a great example of an object for usage I feel neither truly addressed what is going on in this instance and may confuse future readers or instill a bad practice as not explicitly stating a function/method is intended to take a variable amount of arguments/parameters.
function varyArg () {
return arguments[0] + arguments[1];
}
When another developer is looking through your code is it very easy to assume this function does not take parameters. Especially if that developer is not privy to the arguments keyword. Because of this it is a good idea to follow a style guideline and be consistent. I will be using Google's for all examples.
Let's explicitly state the same function has variable parameters:
function varyArg (var_args) {
return arguments[0] + arguments[1];
}
Object parameter VS var_args
There may be times when an object is needed as it is the only approved and considered best practice method of an data map. Associative arrays are frowned upon and discouraged.
SIDENOTE: The arguments keyword actually returns back an object using numbers as the key. The prototypal inheritance is also the object family. See end of answer for proper array usage in JS
In this case we can explicitly state this also. Note: this naming convention is not provided by Google but is an example of explicit declaration of a param's type. This is important if you are looking to create a more strict typed pattern in your code.
function varyArg (args_obj) {
return args_obj.name+" "+args_obj.weight;
}
varyArg({name: "Brian", weight: 150});
Which one to choose?
This depends on your function's and program's needs. If for instance you are simply looking to return a value base on an iterative process across all arguments passed then most certainly stick with the arguments keyword. If you need definition to your arguments and mapping of the data then the object method is the way to go. Let's look at two examples and then we're done!
Arguments usage
function sumOfAll (var_args) {
return arguments.reduce(function(a, b) {
return a + b;
}, 0);
}
sumOfAll(1,2,3); // returns 6
Object usage
function myObjArgs(args_obj) {
// MAKE SURE ARGUMENT IS AN OBJECT OR ELSE RETURN
if (typeof args_obj !== "object") {
return "Arguments passed must be in object form!";
}
return "Hello "+args_obj.name+" I see you're "+args_obj.age+" years old.";
}
myObjArgs({name: "Brian", age: 31}); // returns 'Hello Brian I see you're 31 years old
Accessing an array instead of an object ("...args" The rest parameter)
As mentioned up top of the answer the arguments keyword actually returns an object. Because of this any method you want to use for an array will have to be called. An example of this:
Array.prototype.map.call(arguments, function (val, idx, arr) {});
To avoid this use the rest parameter:
function varyArgArr (...var_args) {
return var_args.sort();
}
varyArgArr(5,1,3); // returns 1, 3, 5
Use the arguments object when inside the function to have access to all arguments passed in.
Be aware that passing an Object with named properties as Ken suggested adds the cost of allocating and releasing the temporary object to every call. Passing normal arguments by value or reference will generally be the most efficient. For many applications though the performance is not critical but for some it can be.
Use array and then you can use how many parameters you need. For example, calculate the average of the number elements of an array:
function fncAverage(sample) {
var lenghtSample = sample.length;
var elementsSum = 0;
for (var i = 0; i < lenghtSample; i++) {
elementsSum = Number(elementsSum) + Number(sample[i]);
}
average = elementsSum / lenghtSample
return (average);
}
console.log(fncAverage([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])); // results 5.5
let mySample = [10, 20, 30, 40];
console.log(fncAverage(mySample)); // results 25
//try your own arrays of numbers
I am new to d3 and try to learn it by reading its source code. I start with probably the simplest function d3.min(). However, my hair was torn apart by a seemingly very common code f(array[i], i, array).
I think f() is supposed to be a function;
array[i] is accessing the element of array at index i;
i is an index of the array;
array is an array of numbers given by user.
If the above 4 understandings are correct, then f() as a function given by the user, must have all three of array[i], i, array as its arguments. But we don't have to use all of these arguments, right?
What is the point of this f()? Can anyone offer any useful example/usage of d3.min(array, f) in which f is not null?
There is a little confusion here. First, d3.min and d3.max can be used with or without an accessor. In the API:
d3.min(array[, accessor])
This square bracket before the comma means that it is optional, not compulsory. So, you can have a simple:
var something = d3.max(someArray);
Or, using an accessor (as you asked, an example where the accessor is not null):
var something = d3.max(data, function(d) {
return d.foo
});
Now we come to your question: you can see in the code above that the accessor function has only 1 argument.
The example you provided is the source code D3 uses to deal with the accessor function. If you look at the code, you'll see array[i], i and array. Those 3 arguments are not provided by the user, but passed to the accessor function by the D3 source code.
Remember that in JS you can pass more arguments than parameters or less arguments than parameters.
The f is a callback function. We use callbacks all the time in JavaScript. So this function works the same way as the map or forEach method does on standard Arrays. It also has the same call signature of value, index and array.
d3.min([1,2,3], (v,i,arr)=>10-v ) // 7
d3.min([1,2,3]) //1
When we call the min function with a 'callback' like the above the answer is the third item in that array but gives the answer as 7 (because 10 - 3 is 7)
adding a f function to find the minimum value of the array while this minimum value being >= 6
var array = [10, 2, 3, 4, 5];
// find the minimum while >= 6
var f = function(x, y, z) {
if (x >= 6) {
return x;
}
}
var min = d3.min(array, f);
console.log(min);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
I'm working my way through "Learning jQuery" (Third Edition).
In Chapter 4: "Manipulating the DOM" there is a section explaining something called the "Value Callback". This is a new one for me.
The author explains this via an example of list of links wherein the ID's of each must be unique.
From the book:
"A value callback is simply a function that is supplied instead of the value for an argument. This function is then invoked once per element in the matched set. Whatever data is returned from the function is used as the new value for the attribute. For example, we can use this technique to generate a different id value for each element, as follows:"
Chaffer, Jonathan (2011-09-23). Learning jQuery, Third Edition (p. 116). Packt Publishing. Kindle Edition.
jQuery(document).ready(function($){
// get all external links
$('div.chapter a').attr({
rel:'external',
title:'Learn more at Wikipedia',
id: function ( index, oldValue ) {
return 'wikilink-' + index;
}
});
})
Works like a charm, but the mechanics of the id: property escape me.
How does parameter 1 (index) know to be an integer?
How does the function know to increment index?
How does the second parameter (oldValue) know to hold the old value of the property (before modification)?
Is this a jQuery construct? A JSON thing? It's cool. it works, but ...what the heck is this "value callback" thing made of?
Please advise
1) How does parameter 1 (index) know to be an integer?
jQuery passes an integer.
2) How does the function know to increment index?
The callback doesn't increment index, the jQuery method does.
3) How does the second parameter (oldValue) know to hold the old value of the property (before modification)?
jQuery passes it.
The answers to questions 1-3 are perhaps best understood by a function that performs something similar to $.attr:
Array.prototype.each = function (f) {
var i;
for (i=0; i < this.length; ++i) {
f(i, this[i]);
}
};
['zero', 'one', 'two'].each(function (i,item) {console.log({i: item})});
f is a callback. each is responsible for iterating over a collection and calling f for each index & item. The same code structure can be used for functions:
/* Map each item in a sequence to something else,
* returning a new sequence of the new values.
*/
Array.prototype.map = function (f) {
var i, result = [];
for (i=0; i < this.length; ++i) {
result[i] = f(i, this[i]);
}
return result;
};
['zero', 'one', 'two'].map(function(i,item) {return item.length});
// result: [4, 3, 3]
/* Return a sequence of the items from this sequence
* for which 'keep' returns true.
*/
Array.prototype.filter = function (keep) {
var i, result = [];
for (i=0; i < this.length; ++i) {
if (keep(i, this[i])) {
result.push(this[i]);
}
}
return result;
};
['zero', 'one', 'two'].filter(function(i,item) {return item.length <= 3});
// result: ['one', 'two']
Implementation of mapconcat, foldl and foldr left as an exercise. As another exercise, rewrite map and filter in terms of each.
Note these functions are merely intended to illustrate how callbacks work. They may cause problems in production code.
4) Is this a jQuery construct? A JSON thing? It's cool. it works, but ...what the heck is this "value callback" thing made of?
Callbacks are a generic technique that jQuery makes extensive use of. They're the key feature of functional programming, where functions are data that can be operated on just like other data types. Thus, you have functions that take functions as arguments and can return functions. In certain contexts, callbacks are also known as "continuations" and form the basis of continuation passing style (CPS). This is particularly important for asynchronous function calls [2] (where the function returns before the computation completes, as opposed to synchronous calls), such as is used for Ajax requests. To see some of the power of CPS, read "Use continuations to develop complex Web applications".
The other aspect of this, the "value" in "value callback", is that, as JS is a dynamically typed language (types are associated with data, rather than variables), formal parameters can be bound to objects of any type. A function can then behave differently depending on what is passed. Sometimes this is implemented by examining the type of the argument, which is in effect ad-hoc polymorphism (the function, rather than the language, must handle dispatch). However, parametric polymorphism or (failing that) duck typing should always be preferred over examining argument types. Parametric polymorphism is achieved by ensuring that all types that can be passed to a given function support the same interface (method names, arguments, preconditions, postconditions & so on). For example, all sequence types should have a length property and be indexed by integers; as long as that holds, you can use your own sequence type with many functions that take arrays.
I'm not sure what you mean by JSON, but it's probably not what is generally meant. JSON is a data interchange format based on a limited version of the JS object literal syntax. JSON is not involved anywhere in the sample code or quoted text.
It's a JQuery construct. If you look at the source, you will find that JQuery is inspecting the parameter in order to learn whether you passed a value or a function. If it's a function, it handles as you see.
I am getting more in to javascript development, and want to ensure I am following popular conventions.
Currently I have a library which consists of functions that can be passed either 1 model to operate on, or many models.
Given the climate that a few javascript libraries are very popular, I am curious; would I be conforming to the 'defacto standard' by achieving my 'single-item or list-of' requirement, by enumerating the arguments variable, or by allowing one of the arguments to be an array?
Scenario 1: argument enumeration
// passing a single entity to my function
sendMail( email, recipient1 );
// passing multiple entities to my function
sendMail( email, recipient1, recipient2 );
Scenario 2: entity argument is either single instance, or array
// pass a single entity
sendMail( email, recipient1 );
// passing multiple entities
sendMail( email, [recipient1, recipient2] );
I have seen areas of jQuery which use 'scenario 2', but I would still like to ask - which approach is the most popular, and why?
Thanks
[EDIT]
A couple of comments have followed the same vein, of using an arguments object - which is similar to 'scenario 2' - but I feel it introduces unnecessary complexity - the elements dont need to be named, because they are just a variable length list. I thought I would just add that here in case my question wasn't clear enough.
[EDIT]
I see code like this all through jQuery-1-7.js
queue: function( elem, type, data ) {
var q;
if ( elem ) {
type = ( type || "fx" ) + "queue";
q = jQuery._data( elem, type );
// Speed up dequeue by getting out quickly if this is just a lookup
if ( data ) {
if ( !q || jQuery.isArray(data) ) {
q = jQuery._data( elem, type, jQuery.makeArray(data) );
} else {
q.push( data );
}
}
return q || [];
}
}
[EDIT]
After some discussion with JP, I came up with this - which I'm not saying is the right choice, but it is very flexible...
lastArgumentAsParams: function()
{
var callerArgs = jQuery.makeArray(this.lastArgumentAsParams.caller.arguments);
// return empty set if caller has no arguments
if ( callerArgs.length == 0 )
return [];
callerArgs.splice(0, callerArgs.length - 1)
// remove all but the last argument
if ( callerArgs.length == 1 && jQuery.isArray(callerArgs[0]))
return callerArgs[0];
else
return callerArgs;
}
If you call this function at the beginning of any function - it will treat the last arg in the caller as a 'variable length argument' - supporting any of the conventions.
For example, I can use it like this
function sendEmail( body, recipients )
{
recipients = lastArgumentAsParams();
// foreach( recipient in recipients )...
}
Now, I can call 'sendEmail' in any of the following ways and it will work as expected
sendEmail('hello world', "bill#microsoft.com" );
sendEmail('hello world', "bill#microsoft.com", "steve#apple.com" );
sendEmail('hello world', ["bill#microsoft.com", "steve#apple.com"] );
I personally prefer using object literals for arguments to support named params, like this:
var myfunc = function(params){ //same as: function myfunc(params){....
alert(params.firstName);
alert(params.lastName);
};
myfunc({firstName: 'JP', lastName: 'Richardson'});
I think that it makes code very readable and order won't matter.
OR
You can also access the arguments object. Note, it's not an array, but it's "array-like". You can read about it here: http://javascriptweblog.wordpress.com/2011/01/18/javascripts-arguments-object-and-beyond/
Edit:
You seem to have a misunderstanding here. You're using the phrase "arguments object" and are thinking that it's the same as object literal notation. They are not.
The arguments object allows you to do this:
function myfunc(){
alert(arguments[0]); //JP
alert(arguments[1]); //Richardson
}
myfunc('JP', 'Richardson');
Does that help?
Another common way is to use object literal as variables:
myFunction(true, {option: value, option2: value});
I personally prefer this method for it is more verbose, and with javascript loose types, it gives you a better hint for what this variables is, and ignores order.
Backbone.js is using this as the preferred method.
To expand on the other answers, there are two main alternatives I usually see: optional arguments and keyword arguments. I don't remember seeing any good examples of the "array-using" idiom and it is kind of obsolete given how the arguments array is always available anyway.
Anyway, my rule of thumb is.
If I have many arguments, or the argument list is likely to change, or if the arguments don't have a good natural order, use the named arguments pattern
My favorite part about this style is that it is really flexible and future proof, while also being kind of self-documenting (in a smalltalk style).
foo({x1:'1', x2:'2', x3:'3'});
function foo(kwargs){
//I try to always copy the arguments back into variables.
//Its a little verbose but it helps documentation a lot and also
// lets me mutate the variables if I want to
var x1 = kwargs.x1,
x2 = kwargs.x2,
x3 = kwargs.x3;
}
If I have few arguments, that are not likely to change, and have a natural order to them, use a plain function (with the optional arguments last in the order)
foo(x1, x2);
foo(x1, x2, x3);
There are three main variations I can think right now of how to handle the optional arguments in the function:
var foo = function(x1, x2, x3){
//variation 1: truthy/falsy
// Short, but I tend to only use it when the variable stands
// for an object or other always-truthy kind of value
x3 = x3 || 'default_value';
//variation 2: using a special placeholder value for blank arguments.
// Usually this is null or undefined. (and undefined works if the arg is not passed too)
if(typeof x3 === 'undefined'){ x3 = 'default_value'; }
//variation 3: explicitly check the number of arguments
// I really like this one since it makes clear if the argument was passed or not.
if(arguments.length < 3){ x3 = 'default_value'; }
}
Also, there are so things I try to avoid:
Don't have functions that receive a large argument list. It can become a mess if they start becoming optional and you forget the order
foo(1, 2, null, null, 3, null, null); //ugh
Don't use fixed-length arrays to be tricky. They are redundant with no arrays at all and when I see an array I usually expect it to 1) be homogeneous and 2) be able to be as long as I want to
foo(true, [1, 2]); //should be foo(true, 1, 2)