Reputation: 7133
In THIS article, I do not understand the following statement:
Note that the inner function cannot call the outer function’s arguments object, however, even though it can call the outer function’s parameters directly.
Can someone please help me understand both the points mentioned
Upvotes: 5
Views: 1585
Reputation: 27282
In the body of every function, there's a special array-like object called arguments
that contains all of the actual arguments the function was invoked with. For example:
function f() {
console.log(arguments);
}
f('one', 2, 3.0);
Most languages would require you to declare those arguments, but JavaScript doesn't care how many arguments you use.
Since there's only one arguments
variable in scope at any given time (at most), it makes sense that you can't access the arguments
variable from an enclosing function:
function f() {
function g() {
console.log(arguments); // will log ['blue'], not ['red']
}
g('blue');
}
f('red');
Upvotes: 3
Reputation: 1074148
The keys to understanding both of those statements are:
Understanding what arguments
is
Understanding "shadowing"
So let's talk about arguments
, then talk about shadowing, and then I think it'll be fairly clear what those two statements are trying to say.
arguments
The arguments
object is provided as part of the scope within a function: It's effectively an automatically-defined variable that has a pseudo-array of the arguments the function was called with:
function foo() {
console.log("My arg count: " + arguments.length);
}
foo(1, 2, 3); // My arg count: 3
When I say automatically-defined, I mean it's very much as though you had this:
// CONCEPTUAL, not real
function foo() {
var arguments = /*...magic code to get the arguments we were called with */;
console.log("My arg count: " + arguments.length);
}
foo(1, 2, 3); // My arg count: 3
...it's just that the JavaScript engine does it for you.
The arguments
object is a lot like an array — as we saw above, it has length
, and you can access the individual arguments using the usual []
notation:
function foo() {
var n;
console.log("My arg count: " + arguments.length);
for (n = 0; n < arguments.length; ++n) {
console.log(arguments[n]);
}
}
foo(1, 2, 3);
Output:
My arg count: 3 1 2 3
You can even use named arguments and the arguments
pseudo-array in the same function:
function foo(a) {
console.log("a = " + a);
console.log("arguments[0] = " + arguments[0]);
}
foo("Hi there");
Output:
a = Hi there arguments[0] = Hi there
It's a pseudo-array because although it has length
and []
indexing, it's not really an array and doesn't have all of the features normal arrays have (like slice
, forEach
, etc.).
In loose mode, it also has the very surprising property that it's linked to the named arguments, so you get weird stuff like this:
function foo(a) {
console.log("before: a = " + a);
arguments[0] = a * 2;
console.log("after: a = " + a);
}
foo(10);
Output:
befer: a = 10 after: a = 20
Spooky, eh? In strict mode, that link isn't there (because it's expensive for the engine to do that).
Okay, so that's arguments
. What's this "shadowing" thing?
Functions create a "scope" for variables and such to exist in, and since you can have functions inside other functions, you can have scopes inside scopes. Functions inside other functions have access to the scopes that contain them, so:
function outer() {
var a = 10;
// `a` exists here
function inner() {
var b = 20;
// both `a` and `b` exist here
console.log("a = " + a + ", b = " + b);
}
// only `a` exists here
inner();
}
a
exists in the scope of outer
, which inner
has access to. b
only exists within inner
.
That's fine as long as we use different names, but suppose we used a
for both of them:
function outer() {
var a = 10;
// `a` exists here
function inner() {
var a = 20;
// `a` exists here -- but which one is it?
console.log("a = " + a);
}
inner();
}
There, since inner
has its own a
variable, it can't access outer
's a
anymore — inner
's a
shadows (hides) outer
's a
.
This is true for variables, named arguments, functions created within a scope, anything that gives a name a meaning within a scope.
So what does shadowing have to do with the two statements? Let's find out...
"Call" is the wrong word here; "access" would make more sense.
Remember our conceptual example above, where arguments
is basically an automatically-declared variable?
// CONCEPTUAL, not real
function foo() {
var arguments = /*...magic code to get the arguments we were called with */;
console.log("My arg count: " + arguments.length);
}
foo(1, 2, 3); // My arg count: 3
Now that you know about shadowing, you can probably see where this is going. This code:
function outer() {
function inner() {
console.log("Inner arg count: " + arguments.length);
}
console.log("Outer arg count: " + arguments.length);
inner();
}
outer(1, 2, 3);
shows
Outer arg count: 3 Inner arg count: 0
...because inside inner
, arguments
refers to the arguments
object for the inner function, not the outer one. It's exactly like they both declared a local variable with the same name: The variable in inner
shadows (hides) the one in outer
.
Now, "Inner function cannot call the outer function’s arguments object" isn't really true. The inner function can access the outer function's arguments
object, but not by that name — if outer
assigns it to a variable, though, there's no problem:
function outer() {
var outerArgs = arguments;
function inner() {
console.log("Outer's arg count from inner: " + outerArgs.length);
}
console.log("Outer arg count: " + arguments.length);
inner();
}
outer(1, 2, 3);
shows
Outer arg count: 3 Outer's arg count from inner: 3
This statement is incomplete: inner
can access outer
's arguments provided inner
doesn't shadow them. So this is fine:
function outer(a) {
function inner() {
console.log("Outer's a = " + a);
}
inner();
}
outer(10);
Output:
Outer's a = 10
...but here, a
gets shadowed and so inner
only sees its own a
, not outer
's:
function outer(a) { // <== declares a named argument `a`
function inner(a) { // <== ALSO declares a named argument `a`
console.log("a = " + a);
}
inner();
}
outer(10);
Output:
a = undefined
So really, both statements are just about scope and shadowing.
Upvotes: 9
Reputation: 262474
- Inner function cannot call the outer function’s arguments object
If you have
function outer(){
function inner(){
doSomething(arguments);
}
}
then arguments
refers to the arguments of the function immediately containing the line. So to the arguments of inner
. You cannot get to the arguments of outer
from there.
- Inner function can call the outer function’s parameters directly.
function outer(a,b,c){
function inner(d,e,f){
doSomething(a,b,c);
}
}
As long as you keep the argument (and local variable) names distinct, you can still access the parameters to outer
from inside inner
(here, those are a
, b
, and c
).
Upvotes: 1