Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function context when calling via arguments

Tags:

javascript

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?

like image 933
Sergey Lk Avatar asked Oct 21 '20 11:10

Sergey Lk


People also ask

How can you call a function and assign the this context to an object?

call() and . apply() methods allow you to set the context for a function.

What is the context of 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.

How do you use an argument in a function?

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.

Can functions accept any number of arguments?

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.


2 Answers

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.

like image 69
Nick Parsons Avatar answered Oct 08 '22 19:10

Nick Parsons


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!

like image 20
Ben Aston Avatar answered Oct 08 '22 21:10

Ben Aston