DEV Community

Cover image for Does JS function's 'this' have a default value?
Andrey Smolko
Andrey Smolko

Posted on

Does JS function's 'this' have a default value?

Disclaimer

There are 1000000 articles about this in JS. I think it is perfectly fine to add one more.

Second parameter of Array.prototype.map

Recently I have checked the MDN page about .map() method of the array prototype and met one paragraph witch hooked my attention:

second params in .map()

where thisArg is a second parameter of .map() method (1)

The second sentence says - "Otherwise, the value undefined will be used as its this value." So, let's do a quick test and pass inside .map() only a callback:

[1,2,3].map(function(){console.log(this)})
// globalObject
// globalObject
// globalObject
Enter fullscreen mode Exit fullscreen mode

It seems confusing as this value is a global object and not undefined when a callback is executing. Actually it is expected as the callback is executed in non-strict mode. Why then the phrase says undefined?

Nevertheless, the phrase makes sense, especially, if we pay attention to the line - "The this value ultimately observable by callbackFn".

So, what "ultimately observable" does mean?

Default value of function's this

Let's create a simplest function ever and try to call it by .apply() method. The .apply() method allows us to set this value explicitly and let's set this as undefined

function f (){
   console.log(this)
}

f.apply(undefined)
// globalObject
Enter fullscreen mode Exit fullscreen mode

So, it is possible to conclude that function f ultimately observes globalObject value in this and not undefined value we explicitly pass. In my opinion we may compare such behaviour with default function parameters:

function f(a=globalObject){
    console.log(a)
}

f();
Enter fullscreen mode Exit fullscreen mode

Spec time!

Now it is time to open the ES specification just to be sure that we do not miss any cases.

I really would like to make your life easier, so there is an abstract operation OrdinaryCallBindThis which is responsible for default this value. There are several steps but 2 of them are main:

OrdinaryCallBindThis steps

Let's pay attention to 2 variables on the pic:

  • thisArgument - is a passed value in a function;
  • thisValue - is an ultimately observable value by a function;

Step 5 says that if our function is called in strict mode then thisValue becomes thisArgument and default value for this is not applicable.

function f (){
   'use strict'
   console.log(this)
}

f.apply(undefined)
// undefined
Enter fullscreen mode Exit fullscreen mode

Step 6 says that if our function is called in non-strict mode and thisArgument equals to undefined or null then thisValue (actual value of this inside function) is globalObject (globalEnv.[[GlobalThisValue]] on the pic is just a global object).

function f (){
   console.log(this)
}

f.apply(undefined)
f.apply(null)
// globalObject
// globalObject
Enter fullscreen mode Exit fullscreen mode

So null value is also substitute with globalObject default value.

Also it is worth to have a look at 6.b which is also valid for non-strict mode. There is call of toObject() operation which creates wrapper objects (String, Number, etc) for primitive values of thisArgument:

function f (){
   console.log(this)
}

f.apply('')
// String {''}
Enter fullscreen mode Exit fullscreen mode

Therefore function's this value is always an object type for non-strict mode.

Conclusion

Each time you call a function in JS you pass a this value in it. Even if you call a function like f(); you still implicitly pass undefined value. A strict mode function accepts any this value without any modifications. A non-strict function substitutes undefined and null values with global object and also transform all primitive values in objects.

Feel free to use that conclusion to win a heart of your next interviewer.

P.S.
There is one my old post about this value in a setTimeout callback. In a browser callback's this is always window and it does not depend on strict/non-strict mode. The reason is that setTimeout method passes window object not undefined as this when executes a callback. Non-strict default substitution just does not work as thisArgument is neither undefined nor null.

(1) - Array.prototype.map() method has a second optional parameter:

// Callback function
map(callbackFn)
map(callbackFn, thisArg)
Enter fullscreen mode Exit fullscreen mode

that second parameter may be used instead of .bind() method for a callback function.

Top comments (3)

Collapse
 
joelbonetr profile image
JoelBonetR 🥇 • Edited

Love that kind of posts! 😁

It's useful to dig deep into core API features

Collapse
 
smlka profile image
Andrey Smolko

Thanks=) Appreciate it!

Collapse
 
paloolap profile image
paloOlap

Wow, did not know about .apply(undefined)!
Cool stuff!