Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why use (function(){}).call(this);? [duplicate]

Tags:

javascript

I've been studying some JS libraries lately that were written by people who really knew what they were doing, and I keep seeing this pattern, and I can't find information about it. I read the docs on the .call() method, but it didn't really make sense to me. I'm hoping to get one of those good old classic in-depth SO explanations with examples.

(function(undefined){    /*(insert entire library here)*/ }).call(this); 

What is this about? Why is this a good way to write a library?

Note, sometimes the undefined is omitted, though I have no idea what difference it makes to put it there or not. I don't even know where the arguments are coming from, or who the caller is.

like image 216
temporary_user_name Avatar asked Sep 23 '13 22:09

temporary_user_name


1 Answers

Let's disassemble this piece of code.

First off there is an anonymous function with immediate invocation. It's similar to this:

(function () {/**/}).call(); (new Date()).getTime(); // timestamp since 1970 jan 1 in milliseconds 

We don't assign new Date() to a variable, instead we use it immediately.


Now why use .call instead of just ()?

.call is a method all Functions have. The first argument is what this will be bound to, subsequent arguments will be passed as arguments to the function. So:

(function () {     console.log(this.foo); // bar }).call({ "foo": "bar" }); 

This works in conjunction with undefined (see below).

.call is the same as .apply with one minor difference. .apply only takes 2 arguments, where the 2nd is an array of arguments. This would be similar:

(function () {}).call(this, "foo", "bar"); (function () {}).apply(this, [ "foo", "bar" ]); 

A common use of apply is in conjunction with the magic variable arguments.

(function () {      console.log(Array.prototype.slice.call(arguments, 1)); // [ "bar" ] })([ "foo", "bar" ]); 

Array.prototype.slice.call(arguments, 1) may look scary but really it's just arguments.slice(1), but arguments isn't an Array so it doesn't have a slice function. We borrow Arrays slice function and use .call to set the this to arguments. Array.prototype.slice(arguments, 1??) is incorrect.


Now why is there this in .call(this)? this always points to the context you're in. If you're in an instance of a class it will point to the instance and if you're in the global scope it will point to that. In a browser environment it is also window.


Why undefined? Since we did a .call(this) with no second argument, all arguments to our anonymous function are undefined. I'm not really sure why you need to make an explicit variable named undefined there. Maybe this is support for some browsers or some lint tool that likes to see undefined defined.

Thanks to @TedHopp. undefined is fickle.

var undefined = "foo"; console.log(undefined); // undefined  (function (undefined) {     console.log(undefined); // "foo" })("foo"); 

You can just as easily have:

(function () {     /* code here */ }()); 

This is completely valid and works just the same. There might be some performance or linting benefits to use the form you posted.

like image 178
Halcyon Avatar answered Sep 21 '22 03:09

Halcyon