Here is the test code:
function fn(i, j) {
arguments = [3, 4];
console.log(i, j); // 1, 2
console.log(arguments); // [3, 4]
}
fn(1, 2);
in the function , I reset the arguments object
However, the arguments object is changed, but the formal parameter which passed in is not chaned, I think they are the same, so what's the differences?
arguments is an array-like object, which has reference to all the parameters passed to it. Here, you have merely reassigned arguments to point to some other object. If you want to change the actual parameters, you have to change the elements of arguments, like this
function fn(i, j) {
arguments[0] = -1;
console.log(i, j); // -1, 2
console.log(arguments); // { '0': -1, '1': 2 }
}
fn(1, 2)
Quoting from arguments, MDN docs
The arguments object is not an Array. It is similar to an Array, but
does not have any Array properties except length. For example, it does
not have the pop method.
However it can be converted to a real Array:
var args = Array.prototype.slice.call(arguments);
If Array generics
are available, one can use the following instead:
var args = Array.slice(arguments);
You are replacing the arguments array (actually an array-like object) with a new array. They are separate objects and exist side by side. Replacing the reference of the arguments variable doesn't make the function use the new array as arguments, it still has an internal reference to the original object.
If you change the contents of the arguments object, it will reflect on the function arguments:
function fn(i, j) {
arguments[0] = 3;
arguments[1] = 4;
console.log(i, j); // 3, 4
console.log(arguments); // [3, 4]
}
fn(1, 2);
Related
My point is this:
function sum(...arguments) {
if (arguments.length === 1) {
const [firstArg] = arguments;
if (firstArg instanceof Array) {
return sum(...firstArg)
}
}
return arguments.reduce((a, b) => a + b);
}
What is this ... are doing infront of arguments. And also please help me know that why const [firstArg] is given and how this is working. And please explain me instanceof in easy words. My native language is not english. Thank you so much.
That's the rest syntax:
The rest parameter syntax allows a function to accept an indefinite number of arguments as an array.
This:
const [firstArg] = arguments;
is destructuring assignment:
[it] makes it possible to unpack values from arrays, or properties from objects, into distinct variables.
Re instanceOf:
The instanceof operator tests to see if the prototype property of a constructor appears anywhere in the prototype chain of an object. The return value is a boolean value.
Unpacking this code:
function sum(...arguments) {
// arguments is an array thanks
// to the rest syntax. If it has a length of
// one...
if (arguments.length === 1) {
// Get the first element
const [firstArg] = arguments;
// If that element is an array
if (firstArg instanceof Array) {
// Call sum again with that element
return sum(...firstArg)
}
}
// Return the sum of the arguments
return arguments.reduce((a, b) => a + b);
}
Your function could be easily simplified by flattening the arguments, and then using reduce to return the sum. And this way you can even have a set of single values passed in, or n multiple arrays as a single argument to the function, or a mixture of both.
function sum(...args) {
return args.flat().reduce((acc, c) => acc + c, 0);
}
console.log(sum(1, 2, 3));
console.log(sum([1], 1, 2, 3));
console.log(sum([1, 2, 3]));
console.log(sum([1, 2], [1, 4, 3]));
console.log(sum([1, 2], [12, 20], [1, 4, 3]));
(...arguments) is the rest parameter syntax and allows a function to accept an indefinite number of arguments as an array, providing a way to represent variadic functions in JavaScript.
for more information, you can read here
const [firstArg] = arguments;
is something named Destructuring assignment
The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables.
you can read more, here
and in the last, The instanceof operator tests to see if the prototype property of a constructor appears anywhere in the prototype chain of an object. The return value is a boolean value
This because ... is a javascript tool that allows you to put multiple parameters in a function it is called the rest parameter function.
It was really confusing for me to read this syntax in Javascript:
router.route('/:id')
.put((...args) => controller.update(...args))
.get((...args) => controller.findById(...args));
What does ...args mean?
With respect to (...args) =>, ...args is a rest parameter. It always has to be the last entry in the parameter list and it will be assigned an array that contains all arguments that haven't been assigned to previous parameters.
It's basically the replacement for the arguments object. Instead of writing
function max() {
var values = Array.prototype.slice.call(arguments, 0);
// ...
}
max(1,2,3);
you can write
function max(...value) {
// ...
}
max(1,2,3);
Also, since arrow functions don't have an arguments object, this is the only way to create variadic (arrow) functions.
As controller.update(...args), see What is the meaning of "foo(...arg)" (three dots in a function call)? .
Essentially, what's being done is this:
.put((a, b, c) => controller.update(a, b, c))
Of course, what if we want 4 parameters, or 5, or 6? We don't want to write a new version of the function for all possible quantities of parameters.
The spread operator (...) allows us to accept a variable number of arguments and store them in an array. We then use the spread operator again to pass them to the update function:
.put((...args) => controller.update(...args))
This is transparent to the update function, who receives them as normal arguments.
The meaning of “…args” (three dots) is Javascript spread operator.
function sum(x, y, z) {
return x + y + z;
}
const numbers = [1, 2, 3];
console.log(sum(...numbers));
// expected output: 6
If you know some Python syntaxes, it is exactly the same as *args. Since *args (Python) is tuple object and Javascript has no tuple like Python, ..args is an Array object.
means pass all values (useful if have unknown# of items)
sample code
console.log(sum(1, 2, 3, 4)); // expected output: 10
function sum(...allItems) {
let total = 0;
for (const item of allItems) {
total += item;
}
return total;
}
It's called 'rest parameter', you can use rest parameter to pass unspecified number of arguments as an array, And a function can have only one rest parameter and it have to be the last parameter for the function
function sum(...args){
let output = 0;
for(const num of args){
output += num;
}
return output;
}
console.log(sum(2,4,8));
here it takes the argument that passed on sum as an array and sum the output and return it
In the example below, I would expect that one could pass the curried function as a predicate to the any function. However, it only produces the correct result when the curried function is wrapped. Why does this behaviour occur?
var isGreaterThanOneCurried = _.curryRight(_.gt, 2)(1);
var isGreaterThanOneNoCurry = function(value) { return value > 1; };
var listOfOnes = [1, 1];
// CORRECT! returns false
_.any(listOfOnes, function(value) { return isGreaterThanOneCurried(value); });
// INCORRECT! returns true
_.any(listOfOnes, isGreaterThanOneCurried);
// CORRECT! returns false
_.any(listOfOnes, isGreaterThanOneNoCurry);
As described in the documentation for _.some (_.any is an alias for _.some):
The predicate is bound to thisArg and invoked with three arguments:
(value, index|key, collection).
Though you have curried from the right, the function still accepts multiple arguments (even though curried functions, by definition, are not supposed to accept multiple arguments, lodash contradicts that definition here). Those multiple arguments are supplied from left-to-right, so what you end up with is an arguments list that looks like this:
[1, 0, [1, 1], 1]
The first three are supplied by _.any, and the final one is one you supplied after right-currying.
You can verify this pretty easily:
var _ = require('lodash')
var listOfOnes = [1, 1]
function logArgsify(fn) {
return function() {
console.log(JSON.stringify(Array.from(arguments)))
return fn.apply(null, arguments)
}
}
var gt = logArgsify(_.gt)
var isGreaterThanOneCurried = _.curryRight(gt, 2)(1)
_.any(listOfOnes, isGreaterThanOneCurried)
// Output: [1,0,[1,1],1]
first argument is 1 (value)
second argument is 0 (index)
third argument is [1, 1] (collection)
fourth argument: 1, supplied earlier
_.gt only considers two arguments
1 > 0 is true
Why does the foo function work fine.
function foo (a,b) {
return arguments.length;
}
But, the boo function returns undefined is not a function, even though I pass arguments in the function.
function boo (a,b) {
return arguments.slice(0);
}
arguments is not an array, but an array-like object. Many array methods work on array-like objects, because they expect an object with numeric properties, and a length property, such as arguments. You can use the built-in functions by using call and passing the object as the this value:
var args = Array.prototype.slice.call(arguments);
//^ args is a real array now
This works with other methods as well, like reduce:
function sum(/*...args*/) {
return [].reduce.call(arguments, function(x, y){return x + y})
}
arguments is an object and not an array. If you print it and see, you can see an object:
function foo (a,b) {
console.log(arguments);
}
foo(10,20);
o/p: { '0': 10, '1': 20 }
From the docs,
This object contains an entry for each argument passed to the
function, the first entry's index starting at 0. For example, if a
function is passed three arguments, you can refer to the argument as
follows:
arguments[0] arguments[1] arguments[2]
It does not have any Array properties except length.
So, return arguments.slice(0); would fail, since slice belongs to the prototype of an array.
I am reading the book "JavaScript-The Good Parts", in chapter 4.14 Curry, the book gives the following example:
Function.method('curry', function(){
var slice = Array.prototype.slice,
args = slice.apply(arguments), //1st-arguments
that=this;
return function(){
return that.apply(null, args.concat(slice.apply(arguments))); //2nd-arguments
}
})
var add1=add.curry(1);
document.writeln(add1(6)); // 7
I have two questions on this code:
There are two places using 'arguments'. In the method invoking in the last two lines code, is it so that '1' goes to the 1st-arguments and '6' goes to the 2nd-arguments?
There is a line of code apply(null, args.concat(slice.apply(arguments))), why does it apply(null,...) here?, what is the sense to apply a argument to a null object?
There are two places using 'arguments'. In the method invoking in the last two lines code, is it so that '1' goes to the 1st-arguments and '6' goes to the 2nd-arguments?
Yes, 1 is part of arguments of the outer function, while 6 goes the arguments in the inner function. The inner function can capture all other variables in a closure except for arguments and this, which have a special meaning inside a function, hence are not part of that closure. So that, and args are captured, but not arguments.
There is a line of code apply(null, args.concat(slice.apply(arguments))), why does it apply(null,...) here?, what is the sense to apply a argument to a null object?
Invoking a method with a null context will simply set the this value to the global object. In the case it seems the author does not care what that context is.
That example was kind of tricky for me too, because I'm not experienced in JavaScript, so I will try to explain it as I would have wished to be explained to me. Due to that reason it might be too explicit!
Also, before you start, I would recommend you to review the 'closure' part (for me to understand closure, was very helpful this quote):
"A closure is a special kind of object that combines two things: a function, and the environment in which that function was created. The environment consists of any local variables that were in-scope at the time that the closure was created." (from Mozilla developer network) - Closures: Line by Line explanation of "Javascript: Good Parts" example?
And now about the curry example:
apply(this, arguments) - this method can be applied to a function to call it and sends the 'this' variable and the parameters variables as an array (arguments).
For example:
// JSON objects
var ob1 = { // first object
a : 0,
b : 0
};
var ob2 = { // the second one
a : 0,
b : 0
};
// a function that updates 'a' and 'b'
function update(newA, newB) {
this.a = newA;
this.b = newB;
}
// 'this' is equal with ob1 and arguments array is [10, 20]
update.apply(ob1, [10, 20]);
// the same as above, but 'this' is here ob2
update.apply(ob2, [30, 40]);
/* after update:
ob1 {
a: 10,
b: 20
}
ob2 {
a: 30,
b: 40
}
*/
Now, that you have understood apply, you might ask why is in this example is called only with a single parameter.
args = slice.apply(arguments);
The function 'slice' takes an array and some parameters that represent indexes and forms a new array only with those.For example:
var anArray = ['red', 'green', 'black'];
var otherArray = anArray.slice(0); // after this line 'otherArray' will be ['red']
otherArray = anArray.slice(1,2); // 'otherArray' will be ['green','black']
// but what happens id you don't specify any indexes?
otherArray = anArray.slice();
// otherArray will be equal with ['red', 'green', 'black'], the same as 'anArray'
// it just makes a copy of it
So, when it does:
args = slice.apply(arguments)
is the same as:
args = slice.apply(/*this*/ arguments, /*arguments*/ undefined);
which is it 'almost' like saying:
args = arguments.slice(); // args will have all the elements the same as arguments
The reason why it does that is because args will be an array with all the methods of an array, but 'arguments' doesn't have any methods.
"Because of a design error, arguments is not really an array. It is an array-like object. Arguments has a length property, but it lacks all of the array methods." (from the book)
Let's recap, we need it these two lines:
var slice = Array.prototype.slice;
args = slice.apply(arguments);
just to make 'arguments' to have all the methods that are specific to the an array.
In our case we will need the 'concat' method that takes two array and joins them.
Function.method('curry', function(){
var slice = Array.prototype.slice;
args = slice.apply(arguments);
that=this; // saves this (which will be a function)
return function(){
// 'that' will be the same as this from above
// and is a function that we call it with the apply
return that.apply(/*this*/null, /*arguments*/ args.concat(slice.apply(arguments)));
// 'this' was null, an arguments was a join between 'args' and 'arguments'
}
});
Finally, you just have to understand how will be used:
function add(a, b) {
return a + b;
}
var add1 = add.curry(1); // add is function, and 'add.curry(1)' returns a function
// that will have 'a' set as 1(one) and 'b' as undefined(not being set)
var sum = add1(6); // 7
// when you call add1(6), you actually call that function that was returned by
// the line add.curry(1), with 'b' equal with 6