I've got this interview question and can't really get what the difference between fn()
an arguments[0]()
since they both reference to the same function.
var length = 10;
function foo() {
console.log(this.length);
}
var obj = {
length: 5,
method: function(fn) {
fn(); // TypeError: Cannot read property 'length' of undefined
arguments[0](); // 2
}
};
obj.method(foo, 1);
Ok, so when calling arguments[0]()
, in foo
it's context (this) will be equal to function arguments - [Arguments] { '0': [Function: foo], '1': 1 }
. But how is this happend?
call() and . apply() methods allow you to set the context for a function.
Context in JavaScript is related to objects. It refers to the object within the function being executed. this refers to the object that the function is executing in.
Arguments are Passed by Value The parameters, in a function call, are the function's arguments. JavaScript arguments are passed by value: The function only gets to know the values, not the argument's locations. If a function changes an argument's value, it does not change the parameter's original value.
When you call a function in JavaScript, you can pass in any number of arguments, regardless of what the function declaration specifies. There is no function parameter limit. In the above function, if we pass any number of arguments, the result is always the same because it will take the first two parameters only.
Consider the following code:
function foo() {
console.log(this.bar);
}
const obj = {
bar: "foobar",
myFunction: foo
}
obj.myFunction(); // foobar
In the above, the this
in the function foo
gets bound to the calling context of foo, in the case above, that is the object obj
, and so this
inside of the function foo
will refer to obj
.
In your case, arguments
is an object. It has a bunch of properties such as its length
as well as numeric properties to represent indexes for the arguments passed into the function. For example, you could (roughly) make your own arguments object like so:
function foo() {
console.log(this.length);
}
const myArguments = {
0: foo,
length: 2
}
myArguments[0](); // 2
Notice that the above myArguments[0]()
is using the same idea of calling a function from the first code snippet (obj.myFunction()
), except we are using bracket notation (arguments[0]
) rather than dot-notation (arguments.0
) to access and call the function stored at the property 0
from the object myArguments
. Thus, the calling context for foo
in this case is myArguments
, and as a result that is what this
gets bound to within the function foo
.
The same idea above applies to your code. When you call fn()
without an explicit context the this
within your foo()
function will become undefined
(if in strict-mode), thus causing your error:
TypeError: Cannot read property 'length' of undefined
However, when using arguments[0]
, the this
gets bound to the arguments
object, much like in the above two examples.
The this
value used by an execution context depends on how a function was called.
1. fn() // TypeError: Cannot read property 'length' of undefined
2. arguments[0]() // 2
Line 1 shows a direct function invocation. For this style the this
value will be undefined in strict mode and the global object in non-strict mode.
Line 2 shows a function being invoked as a method. It is a bit of a trick question because we are more used to seeing the dot operator used with methods, but the bracket property accessor notation works similarly. When you invoke a function as a method, the this
value is set to be the object the method is invoked on. This behavior is designed to support object-oriented style programming.
It's akin to writing arguments.0()
, but the JS syntax does not permit this sequence (presumably due to ambiguity with the decimal place syntax).
Sneaky interview question!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With