Function context when calling via arguments

Viewed 153

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?

3 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.

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!

here obj work like an array, get value on given position and call it as a function

var length = 10;

function foo1() {
  console.log(length);
}

function foo2() {
  console.log(this.length);
}

const obj = {
  length: 5,
  0: foo1,
  1: foo2,
};

obj[0](); // 10
obj[1](); // 5

Related