6

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 3

6

Consider the following code:

function foo() {
  console.log(this.bar);
}

const obj =  {
  bar: "foobar",
  myFunction: foo
}

obj.myFunction(); // foobar
obj["myFunction"](); // also `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 because that's where the function myFunction is being invoked from, and so this inside of the function foo will refer to obj.

In your case, arguments is an object in a similar way to how obj above is an objct. 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() is syntactically invalid, so we can only use bracket-notation like below
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() & obj["myFunction"]()), as 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 (as you're not inovking it from an object by having it prefixed with someObject., or using methods such as .bind(), .call() or .apply()) the this within your foo() function will be 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.

Sign up to request clarification or add additional context in comments.

2 Comments

Why is it specifically 2? The output?
@ZameerAnsari-xameeramir because method(foo, 1) is called with 2 arguments foo and 1. So arguments inside of the method function has a .length of 2. Because arguments[0]() calls foo, it sets foo's this value to be the arguments object of the method function, so the foo function prints the length (number of arguments) passed to method.
4

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!

2 Comments

Yeah, this similarity between calling via dot and brackets (and having method context preserving) may not be obvious. Thanks for your answer!
Why is it specifically 2? The output?
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.