JavaScript performance: Call vs Apply - javascript

Is there a performance benefit in switching from func.apply(obj, params) to func.call(obj) when params is an empty array or null?
I mean, is calling func.call(obj) any faster than calling func.apply(obj, null)?
I'm mostly interested in performance under NodeJS 4.x.
This is for an algorithm that has to make a lot of such calls.

On this page there is a comparison. https://jsperf.com/call-apply-segu
Call was faster on my machine.

Basically, they will do the same steps:
Function.prototype.apply (thisArg, argArray)
If IsCallable(func) is false, then throw a TypeError exception.
If argArray is null or undefined, then
Return the result of calling the [[Call]] internal method of func, providing thisArg as the this value and an empty list of arguments.
Function.prototype.call (thisArg [ , arg1 [ , arg2, … ] ]
)
If IsCallable(func) is false, then throw a TypeError exception.
Let argList be an empty List.
If this method was called with more than one argument then in left to right order starting with arg1 append each argument as the last
element of argList
Return the result of calling the [[Call]] internal method of func, providing thisArg as the this value and argList as the list of arguments.
So the difference, if any, should be implementation dependent, and negligible.

Ha, interesting: it looks like apply is slower than call. 8-)
~/tmp ω cat test.js
function work(a, b, c) {
// do some work
}
var a = [1, 2, 3];
for (var j = 0; j < 4; j++) {
console.time('apply-ing');
for (var i = 0; i < 1000000; i++) {
work.apply(this, a);
}
console.timeEnd('apply-ing');
console.time('call-ing');
for (var i = 0; i < 1000000; i++) {
work.call(this, 1, 2, 3);
}
console.timeEnd('call-ing');
}
~/tmp ω node test.js
apply-ing: 42ms
call-ing: 5ms
apply-ing: 40ms
call-ing: 5ms
apply-ing: 42ms
call-ing: 5ms
apply-ing: 39ms
call-ing: 6ms
~/tmp ω node --version
v4.1.2
~/tmp ω

Related

Returning false from underscore each appears to end the loop

I have encountered some baffling behavior with Underscore's _.each function. I have the following in a base class for a set of objects I'm creating:
constructor(properties = {}, options = {}) {
// lets me replace UUID generator function for testing, and for a
// special-case id used by a singleton subclass
_.defaults(options, {generateUuid});
_.chain(properties)
.keys()
.reject((k) => k === 'id' || k === '_id')
.each((k) => this[k] = properties[k])
.value();
this._id = options.generateUuid();
}
When testing the constructor for my first large subclass, I passed in a large number of properties (numbers, strings, and booleans). My Chai assertion failed at the 43rd property (of 61), which was immediately following the first false value. The error claimed that the property did not exist.
The current state of the subclass in question is simply:
constructor(properties = {}) {
if (typeof properties.subtype !== 'undefined') {
properties._subtype = properties.subtype;
}
// etc. for several more aliased properties
_.defaults(properties, {
_subtype: 'token',
// etc. for default property values
});
super({
_type: 'graphic',
_subtype: properties._subtype,
// etc. for all whitelisted properties
});
}
While trying to debug the problem, I confirmed that all of the properties and their correct values were being passed to the superclass constructor, but that all properties following the first false did not get added to this. It was only when I added trace debugging to the .each that things really changed:
_.chain(properties)
.keys()
.reject((k) => k === 'id' || k === '_id')
.each((k) => {
console.log(k);
this[k] = properties[k];
})
.value();
Suddenly, all of the properties were present in the object and my test passed! Since a single-line arrow function (without brackets) is returning the value of its single expression (while a multi-line arrow function is by default returning undefined), and the expression foo = bar returns bar, my only conclusion is that returning false from _.each was ending the loop.
So, I set out to investigate why that would be the case. I grabbed the Underscore source code (v1.8.3, the same version I'm running) to see if there was special handling specifically to accomplish that result (which would conveniently allow client code to break from an _.each loop):
_.each = _.forEach = function(obj, iteratee, context) {
iteratee = optimizeCb(iteratee, context);
var i, length;
if (isArrayLike(obj)) {
for (i = 0, length = obj.length; i < length; i++) {
iteratee(obj[i], i, obj);
}
} else {
var keys = _.keys(obj);
for (i = 0, length = keys.length; i < length; i++) {
iteratee(obj[keys[i]], keys[i], obj);
}
}
return obj;
};
When context is falsy, optimizeCb(iteratee, context) immediately returns iteratee. There's nothing here that should break out early when iteratee returns false.
Does anyone have an explanation for the behavior I'm observing?
It seems like you are using lodash and not underscore.
From lodash documentation:
The iteratee is invoked with three arguments: (value, index|key, collection). Iteratee functions may exit iteration early by explicitly returning false.

is `.map` not for looping?

I've answered a question here before about How to get number of response of JSON? and I suggested for them to use the map function instead of using a for loop but someone commented that .map is not for looping and to use forEach instead.
Are there any downsides to using map over a for loop?
I also researched this and found a site stating that map > forEach .
Map is used to transform each element in an array into another representation, and returns the results in a new sequence. However, since the function is invoked for each item, it is possible that you could make arbitrary calls and return nothing, thus making it act like forEach, although strictly speaking they are not the same.
Proper use of map (transforming an array of values to another representation):
var source = ["hello", "world"];
var result = source.map(function(value) {
return value.toUpperCase();
});
console.log(result); // should emit ["HELLO, "WORLD"]
Accidentally using .map to iterate (a semantic error):
var source = ["hello", "world"];
// emits:
// "hello"
// "world"
source.map(function(value) {
console.log(value);
});
The second example is technically valid, it'll compile and it'll run, but that is not the intended use of map.
"Who cares, if it does what I want?" might be your next question. First of all, map ignores items at an index that have an assigned value. Also, because map has an expectation to return a result, it is doing extra things, thus allocating more memory and processing time (although very minute), to a simple process. More importantly, it may confuse yourself or other developers maintaining your code.
The map() method creates a new array with the results of calling a
provided function on every element in this array.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
map calls a provided callback function once for each element in an array, in order, and constructs a new array from the results. callback is invoked only for indexes of the array which have assigned values, including undefined. It is not called for missing elements of the array (that is, indexes that have never been set, which have been deleted or which have never been assigned a value).
callback is invoked with three arguments: the value of the element, the index of the element, and the Array object being traversed.
If a thisArg parameter is provided to map, it will be passed to callback when invoked, for use as its this value. Otherwise, the value undefined will be passed for use as its this value. The this value ultimately observable by callback is determined according to the usual rules for determining the this seen by a function.
map does not mutate the array on which it is called (although callback, if invoked, may do so).
The range of elements processed by map is set before the first invocation of callback. Elements which are appended to the array after the call to map begins will not be visited by callback. If existing elements of the array are changed, or deleted, their value as passed to callback will be the value at the time map visits them; elements that are deleted are not visited.
Ref from MDN:
// Production steps of ECMA-262, Edition 5, 15.4.4.19
// Reference: http://es5.github.io/#x15.4.4.19
if (!Array.prototype.map) {
Array.prototype.map = function(callback, thisArg) {
var T, A, k;
if (this == null) {
throw new TypeError(' this is null or not defined');
}
// 1. Let O be the result of calling ToObject passing the |this|
// value as the argument.
var O = Object(this);
// 2. Let lenValue be the result of calling the Get internal
// method of O with the argument "length".
// 3. Let len be ToUint32(lenValue).
var len = O.length >>> 0;
// 4. If IsCallable(callback) is false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (arguments.length > 1) {
T = thisArg;
}
// 6. Let A be a new array created as if by the expression new Array(len)
// where Array is the standard built-in constructor with that name and
// len is the value of len.
A = new Array(len);
// 7. Let k be 0
k = 0;
// 8. Repeat, while k < len
while (k < len) {
var kValue, mappedValue;
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty internal
// method of O with argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if (k in O) {
// i. Let kValue be the result of calling the Get internal
// method of O with argument Pk.
kValue = O[k];
// ii. Let mappedValue be the result of calling the Call internal
// method of callback with T as the this value and argument
// list containing kValue, k, and O.
mappedValue = callback.call(T, kValue, k, O);
// iii. Call the DefineOwnProperty internal method of A with arguments
// Pk, Property Descriptor
// { Value: mappedValue,
// Writable: true,
// Enumerable: true,
// Configurable: true },
// and false.
// In browsers that support Object.defineProperty, use the following:
// Object.defineProperty(A, k, {
// value: mappedValue,
// writable: true,
// enumerable: true,
// configurable: true
// });
// For best browser support, use the following:
A[k] = mappedValue;
}
// d. Increase k by 1.
k++;
}
// 9. return A
return A;
};
}
The best way to think about map is to think of it as a "functional" for loop with some superpowers.
When you call .map on an array, two things happen.
You give it a function, and that function gets called every time it iterates. It passes the item into your function at the current index of the loop. Whatever value you return in this function "updates" that given item in a new array.
The .map function returns you an array of all the values that got returned by the function you give to map.
Let's look at an example.
var collection = [1, 2, 3, 4];
var collectionTimesTwo = collection.map(function (item) {
return item * 2;
});
console.log(collection) // 1, 2, 3, 4
console.log(collectionPlusOne) // 2, 4, 6, 8
On the first line we define our original collection, 1 through 4.
On the next couple line we do our map. This is going to loop over every item in the collection, and pass each item to the function. The function returns the item multiplied by 2. This ends up generating a new array, collectionTimesTwo -- the result of multiplying each item in the array by two.
Let's look at one more example, say we have a collection of words and we want to capitalize each one with map
var words = ['hello', 'world', 'foo', 'bar'];
var capitalizedWords = words.map(function (word) {
return word.toUpperCase();
})
console.log(words) // 'hello', 'world', 'foo', 'bar'
console.log(capitalizedWords) // 'HELLO', 'WORLD', 'FOO', 'BAR'
See where we're going?
This lets us work more functionally, rather than like the following
var words = ['hello', 'world', 'foo', 'bar'];
var capitalizedWords = [];
for (var i = 0; i < words.length; i++) {
capitalizedWords[i] = words[i].toUpperCase();
}
There are a few things that can be said objectively, disregarding the subjective parts.
What is clear and concise use? If I use map() anyone reading the code assumes I'm doing what it says: mapping the values somehow. Being it a lookup table, calculation or whatever. I take the values and return (the same amount of) values transformed.
When I do forEach() it is understood I will use all the values as input to do something but I'm not doing any transformations and not returning anything.
Chaining is just a side effect, not a reason to use one over the other. How often do your loops return something you can or want to reuse in a loop, unless you're mapping?
Performance. Yes, it might be micro-optimization, but why use a function that causes an array to be gathered and returned if you're not going to use it?
The blog post you linked to is quite messy. It talks about for using more memory and then recommends map() because it's cool, even though it uses more memory and is worse in performance.
Also as an anecdote the test linked to there runs for faster than forEach on my one browser. So objective performance cannot be stated.
Even if opinions shouldn't count on SO, I believe this to be the general opinion: use methods and functions that were made for that use. Meaning for or forEach() for looping and map() for mapping.
The phrase "map isn't for looping" was probably a little bit inaccurate, since of course map replaces for-loops.
What the commenter was saying was that you should use forEach when you just want to loop, and use map when you want to collect results by applying an operating to each of the array elements. Here is a simple example:
> a = [10, 20, 30, 40, 50]
[ 10, 20, 30, 40, 50 ]
> a.map(x => x * 2)
[ 20, 40, 60, 80, 100 ]
> count = 0;
0
> a.forEach(x => count++)
undefined
> count
5
Here map retains the result of applying a function to each element. You map when you care about each of the individual results of the operation. In contrast, in your case of counting the number of elements in an array, we don't need to produce a new array. We care only about a single result!
So if you just want to loop, use forEach. When you need to collect all of your results, use map.

Angular equivalent of jQuery $.map?

I'm transitioning from relying on jQuery to building apps in AngularJS. It's recommended in a number of places to not mix jQuery and Angular code.
One thing I miss though is the jQuery $.map function for arrays. I know this could be re-written using the native Javascript map function, but this is not implemented in all browsers (notably, IE < v9).
So, is there an Angular equivalent, or should I got back to writing for (var x = 0; x < foo; x += 1) {...} so I can stop including jQuery?
UPDATE Sometimes knowing what to search for is all you need. Bergie says 'look for polyfills'. Here's a reference guide (from the Modernizr crew) for a bunch of resources for making modern code work on older browsers: HTML5 Cross Browser Polyfills
Check here: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/map
Mozilla has supplied an Array.map polyfill for unsupported browsers
if (!Array.prototype.map) {
Array.prototype.map = function(callback, thisArg) {
var T, A, k;
if (this == null) {
throw new TypeError(" this is null or not defined");
}
// 1. Let O be the result of calling ToObject passing the |this| value as the argument.
var O = Object(this);
// 2. Let lenValue be the result of calling the Get internal method of O with the argument "length".
// 3. Let len be ToUint32(lenValue).
var len = O.length >>> 0;
// 4. If IsCallable(callback) is false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if (typeof callback !== "function") {
throw new TypeError(callback + " is not a function");
}
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (thisArg) {
T = thisArg;
}
// 6. Let A be a new array created as if by the expression new Array(len) where Array is
// the standard built-in constructor with that name and len is the value of len.
A = new Array(len);
// 7. Let k be 0
k = 0;
// 8. Repeat, while k < len
while(k < len) {
var kValue, mappedValue;
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if (k in O) {
// i. Let kValue be the result of calling the Get internal method of O with argument Pk.
kValue = O[ k ];
// ii. Let mappedValue be the result of calling the Call internal method of callback
// with T as the this value and argument list containing kValue, k, and O.
mappedValue = callback.call(T, kValue, k, O);
// iii. Call the DefineOwnProperty internal method of A with arguments
// Pk, Property Descriptor {Value: mappedValue, : true, Enumerable: true, Configurable: true},
// and false.
// In browsers that support Object.defineProperty, use the following:
// Object.defineProperty(A, Pk, { value: mappedValue, writable: true, enumerable: true, configurable: true });
// For best browser support, use the following:
A[ k ] = mappedValue;
}
// d. Increase k by 1.
k++;
}
// 9. return A
return A;
};
}
No, there is no equivalent in Angular. And as you discovered, no you don't need to fall back to writing imperative code.
Instead, just use the map function that is built directly into JavaScript. If you need to support IE8, insert the polyfill at the beginning of your scripts.

Javascript: Having trouble with .map() on IE

This code runs great on Chrome, FFX, etc. It takes the content of a textarea and separates all lines in different array items (new lines are represented by empty array items). When testing it on IE, it throws an error. Code:
This is tregex's value and the call:
var tregex = /\n|([^\r\n.!?]+([.!?]+|$))/gim;
var source = $('#text').val().match(tregex).map($.trim);
The code throws this error message because of .map() (IE only)
User Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1;
Trident/4.0; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR
3.5.30729) Timestamp: Fri, 13 Jul 2012 21:52:48 UTC
Message: Object doesn't support this property or method Line: 128
Char: 6 Code: 0 URI: http://mydomain.com/src/common.js
Why? Any way I can support it on IE7+? (this was tested on IE8).
IE is a terrible browser, and as such doesn't have a built-in map function (at least not in IE7-8). Since you're trying to call map on the results of a Regular Expression match (as opposed to calling it on a jQuery results object), the only map you can use is the built-in one (that IE doesn't have).
There are many libraries that simulate map for you however, including jQuery, Underscore, and Mochikit.
Here's an example of how you could use jQuery's to do what you're trying to do:
$.map($('#text').val().match(tregex), $.trim);
I believe your code may be working in FF and Chrome by accident. Did you mean to use jQuery.map? You are using Array.map which isn't supported in IE prior to 9.
Consider the following as a replacement.
var source = $.map($('#text').val().match(tregex), $.trim);
This uses jQuery's map implementation to loop through and do the trim.
I'm fairly sure Array.map() was only added in IE9. You should loop through the array manually instead.
You can add the support just by including the shim for it:
// Production steps of ECMA-262, Edition 5, 15.4.4.19
// Reference: http://es5.github.com/#x15.4.4.19
if (!Array.prototype.map) {
Array.prototype.map = function(callback, thisArg) {
var T, A, k;
if (this == null) {
throw new TypeError(" this is null or not defined");
}
// 1. Let O be the result of calling ToObject passing the |this| value as the argument.
var O = Object(this);
// 2. Let lenValue be the result of calling the Get internal method of O with the argument "length".
// 3. Let len be ToUint32(lenValue).
var len = O.length >>> 0;
// 4. If IsCallable(callback) is false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if ({}.toString.call(callback) != "[object Function]") {
throw new TypeError(callback + " is not a function");
}
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (thisArg) {
T = thisArg;
}
// 6. Let A be a new array created as if by the expression new Array(len) where Array is
// the standard built-in constructor with that name and len is the value of len.
A = new Array(len);
// 7. Let k be 0
k = 0;
// 8. Repeat, while k < len
while(k < len) {
var kValue, mappedValue;
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if (k in O) {
// i. Let kValue be the result of calling the Get internal method of O with argument Pk.
kValue = O[ k ];
// ii. Let mappedValue be the result of calling the Call internal method of callback
// with T as the this value and argument list containing kValue, k, and O.
mappedValue = callback.call(T, kValue, k, O);
// iii. Call the DefineOwnProperty internal method of A with arguments
// Pk, Property Descriptor {Value: mappedValue, Writable: true, Enumerable: true, Configurable: true},
// and false.
// In browsers that support Object.defineProperty, use the following:
// Object.defineProperty(A, Pk, { value: mappedValue, writable: true, enumerable: true, configurable: true });
// For best browser support, use the following:
A[ k ] = mappedValue;
}
// d. Increase k by 1.
k++;
}
// 9. return A
return A;
};
}
You can also include the es5shim which adds .map and all the other missing array methods in IE7-8 plus other goods like Function#bind

How can I convert the "arguments" object to an array in JavaScript?

Want to improve this post? Provide detailed answers to this question, including citations and an explanation of why your answer is correct. Answers without enough detail may be edited or deleted.
The arguments object in JavaScript is an odd wart—it acts just like an array in most situations, but it's not actually an array object. Since it's really something else entirely, it doesn't have the useful functions from Array.prototype like forEach, sort, filter, and map.
It's trivially easy to construct a new array from an arguments object with a simple for loop. For example, this function sorts its arguments:
function sortArgs() {
var args = [];
for (var i = 0; i < arguments.length; i++)
args[i] = arguments[i];
return args.sort();
}
However, this is a rather pitiful thing to have to do simply to get access to the extremely useful JavaScript array functions. Is there a built-in way to do it using the standard library?
ES6 using rest parameters
If you are able to use ES6 you can use:
Rest Parameters
function sortArgs(...args) {
return args.sort(function (a, b) { return a - b; });
}
document.body.innerHTML = sortArgs(12, 4, 6, 8).toString();
As you can read in the link
The rest parameter syntax allows us to represent an indefinite number of arguments as an array.
If you are curious about the ... syntax, it is called Spread Operator and you can read more here.
ES6 using Array.from()
Using Array.from:
function sortArgs() {
return Array.from(arguments).sort(function (a, b) { return a - b; });
}
document.body.innerHTML = sortArgs(12, 4, 6, 8).toString();
Array.from simply convert Array-like or Iterable objects into Array instances.
ES5
You can actually just use Array's slice function on an arguments object, and it will convert it into a standard JavaScript array. You'll just have to reference it manually through Array's prototype:
function sortArgs() {
var args = Array.prototype.slice.call(arguments);
return args.sort();
}
Why does this work? Well, here's an excerpt from the ECMAScript 5 documentation itself:
NOTE: The slice function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method. Whether the slice function can be applied successfully to a host object is implementation-dependent.
Therefore, slice works on anything that has a length property, which arguments conveniently does.
If Array.prototype.slice is too much of a mouthful for you, you can abbreviate it slightly by using array literals:
var args = [].slice.call(arguments);
However, I tend to feel that the former version is more explicit, so I'd prefer it instead. Abusing the array literal notation feels hacky and looks strange.
It's also worth referencing this Bluebird promises library wiki page that shows how to manage the arguments object into array in a way that makes the function optimizable under V8 JavaScript engine:
function doesntLeakArguments() {
var args = new Array(arguments.length);
for(var i = 0; i < args.length; ++i) {
args[i] = arguments[i];
}
return args;
}
This method is used in favor of var args = [].slice.call(arguments);. The author also shows how a build step can help reduce the verbosity.
function sortArgs(){ return [].slice.call(arguments).sort() }
// Returns the arguments object itself
function sortArgs(){ return [].sort.call(arguments) }
Some array methods are intentionally made not to require the target object to be an actual array. They only require the target to have a property named length and indices (which must be zero or larger integers).
[].sort.call({0:1, 1:0, length:2}) // => ({0:0, 1:1, length:2})
Use:
function sortArguments() {
return arguments.length === 1 ? [arguments[0]] :
Array.apply(null, arguments).sort();
}
Array(arg1, arg2, ...) returns [arg1, arg2, ...]
Array(str1) returns [str1]
Array(num1) returns an array that has num1 elements
You must check number of arguments!
Array.slice version (slower):
function sortArguments() {
return Array.prototype.slice.call(arguments).sort();
}
Array.push version (slower, faster than slice):
function sortArguments() {
var args = [];
Array.prototype.push.apply(args, arguments);
return args.sort();
}
Move version (slower, but small size is faster):
function sortArguments() {
var args = [];
for (var i = 0; i < arguments.length; ++i)
args[i] = arguments[i];
return args.sort();
}
Array.concat version (slowest):
function sortArguments() {
return Array.prototype.concat.apply([], arguments).sort();
}
If you're using jQuery, the following is a good deal easier to remember in my opinion:
function sortArgs(){
return $.makeArray(arguments).sort();
}
In ECMAScript 6 there's no need to use ugly hacks like Array.prototype.slice(). You can instead use spread syntax (...).
(function() {
console.log([...arguments]);
}(1, 2, 3))
It may look strange, but it's fairly simple. It just extracts arguments' elements and put them back into the array. If you still don't understand, see this examples:
console.log([1, ...[2, 3], 4]);
console.log([...[1, 2, 3]]);
console.log([...[...[...[1]]]]);
Note that it doesn't work in some older browsers like IE 11, so if you want to support these browsers, you should use Babel.
Here is benchmark of several methods converting arguments into array.
As for me, the best solution for small amount of arguments is:
function sortArgs (){
var q = [];
for (var k = 0, l = arguments.length; k < l; k++){
q[k] = arguments[k];
}
return q.sort();
}
For other cases:
function sortArgs (){ return Array.apply(null, arguments).sort(); }
Here's a clean and concise solution:
function argsToArray() {
return Object.values(arguments);
}
// example usage
console.log(
argsToArray(1, 2, 3, 4, 5)
.map(arg => arg*11)
);
Object.values( ) will return the values of an object as an array, and since arguments is an object, it will essentially convert arguments into an array, thus providing you with all of an array's helper functions such as map, forEach, filter, etc.
I recommend using ECMAScript 6 spread operator, which will Bind trailing parameters to an array. With this solution you don't need to touch the arguments object and your code will be simplified. The downside of this solution is that it does not work across most browsers, so instead you will have to use a JS compiler such as Babel. Under the hood Babel transforms arguments into a Array with a for loop.
function sortArgs(...args) {
return args.sort();
}
If you can not use a ECMAScript 6, I recommend looking at some of the other answers such as #Jonathan Fingland
function sortArgs() {
var args = Array.prototype.slice.call(arguments);
return args.sort();
}
Lodash:
var args = _.toArray(arguments);
in action:
(function(){ console.log(_.toArray(arguments).splice(1)); })(1, 2, 3)
produces:
[2,3]
Use Array.from(), which takes an array-like object (such as arguments) as argument and converts it to array:
(function() {
console.log(Array.from(arguments));
}(1, 2, 3));
Note that it doesn't work in some older browsers like IE 11, so if you want to support these browsers, you should use Babel.
function sortArg(){
var args = Array.from(arguments); return args.sort();
}
function sortArg(){
var args = Array.from(arguments);
return args.sort();
}
console.log(sortArg('a', 'b', 1, 2, '34', 88, 20, '19', 39, 'd', 'z', 'ak', 'bu', 90));
Another Answer.
Use Black Magic Spells:
function sortArguments() {
arguments.__proto__ = Array.prototype;
return arguments.slice().sort();
}
Firefox, Chrome, Node.js, IE11 are OK.
Try using Object.setPrototypeOf()
Explanation: Set prototype of arguments to Array.prototype
function toArray() {
return Object.setPrototypeOf(arguments, Array.prototype)
}
console.log(toArray("abc", 123, {def:456}, [0,[7,[14]]]))
Explanation: Take each index of arguments , place item into an array at corresponding index of array.
could alternatively use Array.prototype.map()
function toArray() {
return [].map.call(arguments, (_,k,a) => a[k])
}
console.log(toArray("abc", 123, {def:456}, [0,[7,[14]]]))
Explanation: Take each index of arguments , place item into an array at corresponding index of array.
for..of loop
function toArray() {
let arr = []; for (let prop of arguments) arr.push(prop); return arr
}
console.log(toArray("abc", 123, {def:456}, [0,[7,[14]]]))
or Object.create()
Explanation: Create object, set properties of object to items at each index of arguments; set prototype of created object to Array.prototype
function toArray() {
var obj = {};
for (var prop in arguments) {
obj[prop] = {
value: arguments[prop],
writable: true,
enumerable: true,
configurable: true
}
}
return Object.create(Array.prototype, obj);
}
console.log(toArray("abc", 123, {def: 456}, [0, [7, [14]]]))
Benshmarck 3 methods :
function test()
{
console.log(arguments.length + ' Argument(s)');
var i = 0;
var loop = 1000000;
var t = Date.now();
while(i < loop)
{
Array.prototype.slice.call(arguments, 0);
i++;
}
console.log(Date.now() - t);
i = 0,
t = Date.now();
while(i < loop)
{
Array.apply(null, arguments);
i++;
}
console.log(Date.now() - t);
i = 0,
t = Date.now();
while(i < loop)
{
arguments.length == 1 ? [arguments[0]] : Array.apply(null, arguments);
i++;
}
console.log(Date.now() - t);
}
test();
test(42);
test(42, 44);
test(42, 44, 88, 64, 10, 64, 700, 15615156, 4654, 9);
test(42, 'truc', 44, '47', 454, 88, 64, '#ehuehe', 10, 64, 700, 15615156, 4654, 9,97,4,94,56,8,456,156,1,456,867,5,152489,74,5,48479,89,897,894,894,8989,489,489,4,489,488989,498498);
RESULT?
0 Argument(s)
256
329
332
1 Argument(s)
307
418
4
2 Argument(s)
375
364
367
10 Argument(s)
962
601
604
40 Argument(s)
3095
1264
1260
Enjoy !
function sortArgs(...args) {
return args.sort(function (a, b) { return a - b; });
}
document.body.innerHTML = sortArgs(1, 2, 3, 4).toString();
Although rest parameters work well, if you want to continue to use arguments for some reason, consider
function sortArgs() {
return [...arguments].sort()
}
[...arguments] can be considered a sort of alternative to Array.from(arguments), which also works perfectly well.
An ES7 alternative is an array comprehension:
[for (i of arguments) i].sort()
This could be easiest if you want to process or filter the arguments prior to sorting:
[for (i of arguments) if (i % 2) Math.log(i)].sort()
function x(){
var rest = [...arguments]; console.log(rest);return
rest.constructor;
};
x(1,2,3)
I tried simple destructing technique
The Arguments object is only available inside a function body. Although you can index the Arguments Object like an array, it is not an array. It does not have any array properties other than length.
// function arguments length 5
function properties(a,b,c,d,e){
var function_name= arguments.callee.name
var arguments_length= arguments.length;
var properties_length= properties.length;
var function_from= properties.caller.name;
console.log('I am the function name: '+ function_name);
console.log('I am the function length, I am function spacific: '+ properties_length);
console.log('I am the arguments length, I am context/excution spacific: '+ arguments_length);
console.log('I am being called From: '+ function_from );
}
// arguments 3
function parent(){
properties(1,2,3);
}
//arguments length 3 because execution spacific
parent();
Although it can be indexed like an array as you can see in this example:
function add(){
var sum=0;
for(var i=0; i< arguments.length;i++){
sum = sum + arguments[i];
}
return sum;
}
console.log(add(1,2,3));
However, the Arguments object is not an array and does not have any other properties other than length.
You can convert the arguments object into an array at which point you can access the Arguments object.
There are many ways you can access the arguments object inside a function body, and these include:
you can call the Array.prototoype.slice.call method.
Array.prototype.slice.call(arguments)
function giveMeArgs(arg1,arg2){
var args = Array.prototype.slice.call(arguments);
return args
}
console.log( giveMeArgs(1,2));
you can use the array literal
[].slice.call(arguments).
function giveMeArgs(arg1,arg2){
var args = [].slice.call(arguments);
return args;
}
console.log( giveMeArgs(1,2) );
you can use Rest ...
function giveMeArgs(...args){
return args;
}
console.log(giveMeArgs(1,2))
you can use spread [...]
function giveMeArgs(){
var args = [...arguments];
return args;
}
console.log(giveMeArgs(1,2));
you can use Array.from()
function giveMeArgs(){
var args = Array.from(arguments);
return args;
}
console.log(giveMeArgs(1,2));
You can create a reusable function to do it with any arguments, the simplest one is something like this:
function sortArgs() {
return [...arguments].sort();
}
sortArgs('ali', 'reza', 1, 2, 'a'); //[1, 2, "a", "ali", "reza"];
Spread syntax can be used in ES6 and above...
But if you'd like to use something compatible with ES5 and below, you can use Array.prototype.slice.call, so you code looks like this:
function sortArgs() {
return Array.prototype.slice.call(arguments).sort();
}
sortArgs('ali', 'reza', 1, 2, 'a'); //[1, 2, "a", "ali", "reza"];
There are also few other ways to do this, for Example using Array.from or loop through the arguments and assign them to a new array...
This is a very old question, but I think I have a solution that is slightly easier to type than previous solutions and doesn't rely on external libraries:
function sortArguments() {
return Array.apply(null, arguments).sort();
}

Categories

Resources