Array.of() not working as specified in es6? - javascript

According to documentation here, Array.of() is an alternative to new Array() that lets you create an array literal at run-time even if it's only got one element (a case where new Array() breaks down). I like that, and have replaced most new() calls on my custom Array subclass with .of(), but since converting I am now finding errors when I try certain things.
The link claims that .of() is equivalent to this:
Array.of = function() { return Array.prototype.slice.call(arguments); }
To check if that's true, I wrote this test to compare the real of() with that polyfill:
Array.of_polyfill = function( ...args ) // Fake of().
{
console.log( args );
return Array.prototype.slice.call( args );
}
class A extends Array // So we can peek inside what the real of() is doing.
{
constructor( ...args ) // Using of() on an "A" calls this constructor.
{
console.log( args );
return Array.prototype.slice.call( args );
}
}
var a1 = A.of ( 5,6,7,8 );
var a2 = A.of_polyfill( 5,6,7,8 );
Both a1 and a2 ultimately contain the correct value [5,6,7,8], but internally working with the two lines is inconsistent, as shown in the console log:
[4]
(4) [5, 6, 7, 8]
From the first line there, calling .of() did not result in the whole literal being in scope, but just [4], as if internally it's calling the A constructor to preallocate four slots, which is not something the polyfill mentions it does.
I care about the internal behavior because one of the main suggested uses of of() is custom Array subclasses like my A above, to allow me to specify array literals of my new type. To that end my A constructor should be able to do manual stuff with the literal (the arguments that were passed in) but it just can't access it due to the above. Is there some other way that gives me access?
Is that suggested polyfill for of() accurate or am I misunderstanding some aspect of JavaScript and constructors?

Minor point... Array.of lets you create an Array, not an "Array literal". The "literal" refers to the syntax that lets you describe and create an array with its initializing data if any, like var a = [1, 2, 3].
Ultimately, the docs make no claim of complete equivalence of the polyfill. MDN is a wiki, so if the polyfill can be improved, then anyone is welcome to contribute changes. Keep in mind though that it will be expected to work in ES5 and lower, so approaching the full subclassing behavior will be much more work.

So we are mixing multiple things in the question. You are using spread operators where you shouldn't, and there seems to be a misunderstanding about what native code is. Array.of is a static function; creating a constructor doesn't mimic its' behaviour. You can however override the static method and use the spread operator. In this case, it would be
class B extends Array {
static of(...args) {
return args;
}
}
Or using arguments
class A extends Array {
static of() {
return Array.prototype.slice.call(arguments);
}
}
Or just
class A extends Array {
static of() {
return Array.from(arguments);
}
}
Again, your "polyfill" does too much. By using the spread operator, you'll already get an array. This would suffice:
Array.of_polyfill = function (...args) {
return args;
};
Below you'll get three times the same array:
Array.of_polyfill = function (...args) {
return args;
};
class A extends Array {
static of() {
return Array.prototype.slice.call(arguments);
}
}
class B extends Array {
static of(...args) {
return args;
}
}
var a1 = A.of(5, 6, 7, 8);
var a2 = A.of_polyfill(5, 6, 7, 8);
var b = B.of(5,6,7,8);
console.log(a1);
console.log(a2);
console.log(b);
Here's the function declaration in ecmascript6:
/**
#param {...null} items
#static
#return {Array}
*/
Array.of = function(items) {};

You're assuming that your constructor function is called with the arguments that you pass to A.of. That's wrong.
Let's take a look at the specification of Array.of:
Let len be the actual number of arguments passed to this function.
Let items be the List of arguments passed to this function.
Let C be the this value.
If IsConstructor(C) is true, then
a. Let A be ? Construct(C, « len »).
Else,
a. Let A be ? ArrayCreate(len).
[...]
In our case, "C" is A, the class that you defined, which is a constructor. A new instance of A is created, the constructor is called with "len", the number of arguments that we passed to Array.of.
Normally, the constructor would be Array, so Array(len) would be called. Here, A(4) is called. Thus, ...args is [4].
After that, the algorithm will fill the newly constructed array with the correct values.
That means that the results of Array.of and Array.prototype.slice.call are equivalent, but not the way they work internally.

after some tests, it seems that the actual implementation of Array.of() is something like this:
Array.of = function() {
//check wether `this` is a constructor, otherwise fall back to the `Array` constructor
//maybe there's a better way to determine wether `this` is a constructor _before_ calling it.
var constructor = typeof this === "function" && "prototype" in this? this: Array,
result = new constructor(arguments.length);
for(var i=0; i<arguments.length; ++i)
//FF and Chrome use defineProperty()
Object.defineProperty(result, i, {
value: arguments[i],
writable: true,
enumerable: true,
configurable: true
})
//Edge simply assigns the values
//result[i] = arguments[i];
//didn't test IE, Opera, Safari or any mobile browser
//and again, setting the length after the items have been defined
result.length = arguments.length;
return result;
}
Why just "something" - didn't you read the spec?
No, this was driven/deduced by the actual behaviour in the browser. And only "something like", because I might have missed something.
Especially the part with determining wether this is an actual constructor is something I'm not entirely satisfied with. It may produce a false match (and then throw) if you take an arrow function and manually add a property named prototype; but that kind of hacks should be pretty rare.
"After some tests" - what did you test?
how the function behaves in different environments,
attached to functions, to consturctor functions, to functions that ain't constructors, to general objects,
wether there's a difference between attaching it to some Object, or calling it with Function#call,
wether and what arguments are passed to the functions constuctor.
how it behaves if constructor and constructor.prototype.constructor are not the same,
wether it needs Array in the prototype-chain (to fall back to the Array-constructor),
Basically how it determines the constructor function to initialize the returned object
what happens if I predefine the indices and the length-proeprty (it throws an error if the property ain't configurable:true, but only in FF and Chrome)

Related

array.splice = what does it means?

I would like to understand the meaning of that code fragment. "saveTo" is a array, the programmer assigned a function() to the splice method. I don't understand what does it mean. Is that a override? What is the meaning of the return argument?, and why the function takes no argument while splice requires 2 or more arguments?
saveTo.splice = function() {
if (saveTo.length == 1) {
$("#send").prop("disabled", true);
}
return Array.prototype.splice.apply(this, arguments);
};
Javascript lets you re-assign methods at runtime. In this case, what the programmer was doing is reassigning splice on this specific instance of an array in order to call a jQuery method. Beyond that, it works in exactly the same way as the existing splice as they are calling return Array.prototype.splice.apply(this, arguments); - meaning that this method just passes on whatever arguments are passed to it.
Here's a demo:
var myArray = [1,2,3,4];
console.log("Splice before re-assing: ", myArray.splice(1,1));
// reset it.
myArray = [1,2,3,4];
myArray.splice = function(){
console.log("From inside new splice function");
return Array.prototype.splice.apply(this, arguments);
}
console.log("Splice after re-assiging: ", myArray.splice(1,1));
Whether this is a good thing to do is debatable. It breaks a few principles of programming.
The programmer that wrote this code knew that some other part of the program is calling splice on this array, and he wanted to attach an event to that, in order to update the user interface (hence the call to jQuery).
This is commonly called "Monkey Patching". You can read about it at https://www.audero.it/blog/2016/12/05/monkey-patching-javascript/
This is not a good pratice as it obfuscate what is happening: no programmer would expect that calling a data manipulation function has side-effects somewhere else.
You can run this sample to understand how it works:
const myArray = [];
// Patch push method only for this instance of array.
myArray.push = function() {
// log event
console.log('myArray.push was called with the following arguments', arguments);
// Call the original push function with the provided arguments.
return Array.prototype.push.apply(this, arguments);
}
myArray.push(1);
You can also patch methods for all instances of a given class:
// Patch push method on all arrays
const originalPush = Array.prototype.push;
Array.prototype.push = function() {
// log event
console.log('.push was called with the following arguments', arguments);
// Call the original push function with the provided arguments.
return originalPush.apply(this, arguments);
}
const myArray = [];
myArray.push(1);
As for your question about the arguments, in javascript all functions can access the arguments array-like object that contains the arguments the function was called with, which does not depend on which arguments are specified in the original declaration.
function doSomething(arg1) {
console.log(arguments[2]);
}
doSomething(1, 2, 3); // outputs "3"
Here is the MDN documentation about it: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments
Note that there is a better way to extend arrays in ES6:
class CustomArray extends Array {
splice(...args) {
if(this.length === 1) {
$("#send").prop("disabled", true);
}
super.splice(...args);
}
}
Now that there are other ways to change the arrays length, .length, .pop, .shift, etc. so those should be overriden as well. However then it is still questionable wether the code calling those methods should not just cause the side effect.
What this does is it adds some checks for specifically saveTo.splice. If you call anyOtherArray.splice, then it'll just be evaluated as per normal. The reason it takes no arguments is because Array.prototype.splice takes arguments, and also the calling context of saveTo, as well as the array-like objects arguments, representing all the arguments passed to saveTo.splice. So it's just adding a little bit of extra code based on a specific condition - other than that, there's no difference to the native splice.
1) Yes, the programmer has overridden splice method, its not recommended
2) return statement is nothing but calls Array.prototype.splice(the original method).
3) Yes, splice requires arguments, but in JS, you may not define them as function params. You get the passed parameters as an array like object arguments inside your functions,
if you look closely, they call Array.prototype.splice with this and arguments object.
Okay, let's dissect this piece by piece.
saveTo.splice = function() {
if (saveTo.length == 1) {
$("#send").prop("disabled", true);
}
return Array.prototype.splice.apply(this, arguments);
};
As we all know that in JavaScript functions are first class objects, so if we have an object let's say saveTo something like this:
const saveTo = {};
Then we can assign a function to one of its properties like :
saveTo.splice = function() {
};
or something like this to:
const saveTo = {
splice: function() {
}
};
With that out of the way, you are just calling the Array#prototype#splice method to create a shallow copy out of the array and passing it an iterable to it.
So in total you have overridden the native Array#prototype#splice to fit your requirement.

What are the edge cases when using Object.prototype.toString?

So far I have relied on Object.prototype.toString.call(x) to distinguish between the different native object types in Javascript, arrays in particular.
If you subclass arrays, you get some strange behavior:
function Ctor() {}
Ctor.prototype = Object.create(Array.prototype);
var x = new Ctor();
x.push(1);
Object.prototype.toString.call(x); // [object Object]
Probably this is documented in the ES5 specs (and no longer an issue in ES6), but I consider it a quirk of the current version of the language. I adapted my corresponding functions as follows:
function objTypeOf(deep, type) {
return function _objTypeOf(x) {
do {
if (Object.prototype.toString.call(x).slice(8, -1).toLowerCase() === type) return true;
x = Object.getPrototypeOf(x);
} while(deep && x !== null);
return false;
};
}
var arr = objTypeOf(false, "array"),
arrP = objTypeOf(true, "array"); // array prototype
console.log(arr(x)); // false
console.log(arrP(x)); // true
objTypeOf checks the current object and the entire prototype chain until there is a type match. It accepts an object even if merely one of the prototypes matches the expected type. objTypeOf is not based on prototype identities, but on strings (lacking identity).
I wonder now if there are other edge cases when using Object.prototype.toString, that need special treatment?
Well your problem is not with Object.prototype.toString, but that you tried to subclass arrays. It just doesn't work, and toString correctly tells you that you failed to create an array. It's merely an object that has Array.prototype in its prototype chain (if that was what you cared for, use instanceof Array).
Regardless, to answer your title question:
What are the edge cases when using Object.prototype.toString?
Host objects. Everything that is not a native JS object, despite looking like one, might return any [[Class]] value that you didn't expect. There are even known cases where callable objects do not report Function.

What is the difference between Array.prototype.reverse and Array.reverse in Javascript?

The question I have deals with an application of adding a new method to the existing String constructor. In Object Oriented Program for Javascript by Stoyan Stefanov, there is an example of using the Array constructor's .reverse() method to create one for the String constructor. Here is the example:
String.prototype.reverse = function() {
return Array.prototype.reverse.apply(this.split('')).join('');
}
I thought the .reverse() method of Array belonged directly to the object of Array. In fact, when I try to do the second bit of code with this statement:,
String.prototype.reverse = function() {
return Array.reverse.apply(this.split('')).join(''); //WITHOUT the .prototype
}
var rev = "karunesh".reverse(); //applying the code here
I get an error in the Firebug Console stating: "TypeError: missing argument 0 when calling function Array.reverse". That does not make any sense to me.
And of course, if I add back in the .prototype, it works perfectly fine.
Also, if is the case that I have to call upon prototype to access the .reverse() method from the Array object, then is it the case that I have to do that for any built-in object in Javascript?
Thanks for the help in advance!
Is it the case that I have to call upon prototype to access the .reverse() method from the Array object
No. To access a method on an object, just access it with dot notation. What you want to do is simply
return this.split('').reverse().join('');
That is just what apply (or call) does:
var arr = this.split('');
return arr.reverse.apply(arr).join('');
and finally arr.reverse === Array.prototype.reverse since that's where Array objects do inherit from. You are not accessing the reverse method on the Array constructor function object itself, you are to access the property that all Array instances share - via their prototype. Yet you hardly will ever need to use the prototype object explicitly, that's only when you're dealing with objects that are not Array instances (do not share the prototype) like arguments objects or NodeLists.
TypeError: missing argument 0 when calling function Array.reverse. That does not make any sense to me.
Array.reverse is a non-standard Array generic method which is only available in Firefox. It's purpose is to simplify the construct of applying Array prototype methods on other objects, and it does take the array-like object as it's first parameter. An example:
Array.reverse([0, 1]) // [1, 0]
which is equivalent to
Array.prototype.reverse.apply([0, 1]);
However, you were doing
Array.reverse.apply([…]/*, undefined*/)
which is calling the Array.reverse function with the array for the (irrelevant) this value and no actual argument, equivalent to
Array.prototype.reverse.apply(undefined)
and that throws the rightful exception.
Array.reverse is undefined (at least in Chrome 29) - Array.prototype.reverse is a function that will reverse the order of the "iterable" it is called on.
The key thing to note here is that Array is not a class like you would have in Java - rather it is a constructor:
[].constructor === Array;
// true
The prototype property of Array is actually what is providing the behavior to any particular instance of Array:
Object.getPrototypeOf([]) === Array.prototype;
// true
// Bad idea, just for an example
var guys = ['Tom', 'Harry', 'Richard'];
Array.prototype.exclaim = function() {
return this.join(", ") + "?!?!?!";
};
guys.exclaim();
// Tom, Harry, Richard?!?!?!
The key here is that JavaScript uses a prototype-based object-oriented pattern, rather than the classical pattern you are more likely familiar with. Instead of having "classes" which contain all the behaviors, but which are distinct from instances, JavaScript has objects, which can be the "prototypes" of other objects, providing data and behavior to the child objects.
// Totally licit OO pattern in JavaScript
var prototypeClass = {
method1: function() { console.log("Hello from method 1!"); },
method2: function() { console.log("Hello from method 2!"); },
classData: 42
};
var prototypeInstance = Object.create(prototypeClass);
prototypeInstance.method1() // Hello from method 1!
prototypeInstance.classData // 42
// And you can modify the class after
// instantiating instances and the changes
// will be picked up by the instances
prototypeClass.happyPrimes = "Don't they teach recreational mathematics anymore?";
prototypeInstance.happyPrimes // The quote from 42

dynamic object construction in javascript?

When I want to call a function in javascript with arguments supplied from elsewhere I can use the apply method of the function like:
array = ["arg1", 5, "arg3"]
...
someFunc.apply(null, array);
but what if I need to call a constructor in a similar fashion? This does not seem to work:
array = ["arg1", 5, "arg3"]
...
someConstructor.apply({}, array);
at least not as I am attempting:
template = ['string1', string2, 'etc'];
var resultTpl = Ext.XTemplate.apply({}, template);
this does not work wither:
Ext.XTemplate.prototype.constructor.apply({}, template);
Any way to make that one work? (In this particular case I found that new Ext.XTemplate(template) will work, but I am interested in the general case)
similar question but specific to built-in types and without an answer I can use:
Instantiating a JavaScript object by calling prototype.constructor.apply
Thank you.
Edit:
Time has passed and ES6 and transpilers are now a thing.
In ES6 it is trivial to do what I wanted: new someConstructor(...array).
Babel will turn that into ES5 new (Function.prototype.bind.apply(someConstructor, [null].concat(array)))(); which is explained in How to construct JavaScript object (using 'apply')?.
There's no simple, straightforward way to do this with a constructor function. This is because special things happen when you use the new keyword to call a constructor function, and so if you're not going to do that, you have to emulate all of those special things. They are:
Creating a new object instance (you're doing that).
Setting that object's internal prototype to constructor function's prototype property.
Setting that object's constructor property.
Calling the constructor function with that object instance as the this value (you're doing that).
Handling the special return value from the constructor function.
I think that's about it, but worth double-checking in the spec.
So if you can avoid it and just use the constructor function directly, I'd do that. :-) If you can't, though, you can still do it, it's just awkward and involves workarounds. (See also this related answer here on StackOverflow, although I cover all of the ground here [and then some] as well.)
Your biggest issue is #2 above: Setting the internal prototype of the object. For a long time, there was no standard way to do this. Some browsers supported a __proto__ property that did it, so you can use that if it's there. The good news is that ECMAScript 5 introduces a way to do this explicitly: Object.create. So cutting-edge browsers like Chrome will have that. But if you're dealing with a browser that has neither Object.create nor __proto__, it gets a bit ugly:
1) Define a custom constructor function.
2) Set its prototype property to the prototype property of the real constructor function
3) Use it to create a blank object instance.
That handles the prototype for you. Then you continue with:
4) Replace the constructor property on that instance with the real constructor function.
5) Call the real constructor function via apply.
6) If the return value of the real constructor function is an object, use it instead of the one you created; otherwise, use the one you created.
Something like this (live example):
function applyConstruct(ctor, params) {
var obj, newobj;
// Use a fake constructor function with the target constructor's
// `prototype` property to create the object with the right prototype
function fakeCtor() {
}
fakeCtor.prototype = ctor.prototype;
obj = new fakeCtor();
// Set the object's `constructor`
obj.constructor = ctor;
// Call the constructor function
newobj = ctor.apply(obj, params);
// Use the returned object if there is one.
// Note that we handle the funky edge case of the `Function` constructor,
// thanks to Mike's comment below. Double-checked the spec, that should be
// the lot.
if (newobj !== null
&& (typeof newobj === "object" || typeof newobj === "function")
) {
obj = newobj;
}
// Done
return obj;
}
You could take it a step further and only use the fake constructor if necessary, looking to see if Object.create or __proto__ are supported first, like this (live example):
function applyConstruct(ctor, params) {
var obj, newobj;
// Create the object with the desired prototype
if (typeof Object.create === "function") {
// ECMAScript 5
obj = Object.create(ctor.prototype);
}
else if ({}.__proto__) {
// Non-standard __proto__, supported by some browsers
obj = {};
obj.__proto__ = ctor.prototype;
if (obj.__proto__ !== ctor.prototype) {
// Setting it didn't work
obj = makeObjectWithFakeCtor();
}
}
else {
// Fallback
obj = makeObjectWithFakeCtor();
}
// Set the object's constructor
obj.constructor = ctor;
// Apply the constructor function
newobj = ctor.apply(obj, params);
// If a constructor function returns an object, that
// becomes the return value of `new`, so we handle
// that here.
if (typeof newobj === "object") {
obj = newobj;
}
// Done!
return obj;
// Subroutine for building objects with specific prototypes
function makeObjectWithFakeCtor() {
function fakeCtor() {
}
fakeCtor.prototype = ctor.prototype;
return new fakeCtor();
}
}
On Chrome 6, the above uses Object.create; on Firefox 3.6 and Opera, it uses __proto__. On IE8, it uses the fake constructor function.
The above is fairly off-the-cuff, but it mostly handles the issues I'm aware of in this area.
From developer.mozilla:
Bound functions are automatically suitable for use with the new operator to construct new instances created by the target function. When a bound function is used to construct a value, the provided this is ignored. However, provided arguments are still prepended to the constructor call.
That said, we still need to use apply to get the arguments out of an array and into the bind call. Further, we need to also reset bind's function as the this argument of the apply function. This gives us a very succinct one-liner that does exactly as needed.
function constructorApply(ctor, args){
return new (ctor.bind.apply(ctor, [null].concat(args)))();
};
Since the original question is quite old, here's a simpler methods in practically any modern browser (as of writing, 2018, I'm counting Chrome, FF, IE11, Edge...).
var template = ['string1', string2, 'etc'];
var resultTpl = Object.create(Ext.XTemplate.prototype);
Ext.XTemplate.apply(resultTpl, template);
Those two lines also explain how the new operator basically works.
I think another way of achieving this could be to extend the Ext Template class so that the constructor of the new object takes your array and does the stuff you want to do. This way all the constructing stuff would be done for you and you could just call the constructor of your superclass with your arguments.

JavaScript variable number of arguments to function

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

Categories

Resources