DEV Community

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

Posted on

17 9 1

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.

AWS GenAI LIVE image

Real challenges. Real solutions. Real talk.

From technical discussions to philosophical debates, AWS and AWS Partners examine the impact and evolution of gen AI.

Learn more

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!

The best way to debug slow web pages cover image

The best way to debug slow web pages

Tools like Page Speed Insights and Google Lighthouse are great for providing advice for front end performance issues. But what these tools can’t do, is evaluate performance across your entire stack of distributed services and applications.

Watch video

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay