📜 ⬆️ ⬇️

Five ways to call a function

I often have to deal with JavaScript code, errors in which are caused by incorrect understanding of how functions work in JavaScript (by the way, a significant part of this code was written by me). JavaScript is a multi-paradigm language, and it has functional programming mechanisms. It's time to explore these possibilities. In this article I will tell you about five ways to call functions in JavaScript.

In the early stages of learning JavaScript, newbies usually think that functions in it work in much the same way as, say, in C #. But the mechanisms for calling functions in JavaScript have a number of important differences, and ignorance of them can result in errors that will not be easy to find.

Let's write a simple function that returns an array of three elements — the current value of this and two arguments passed to the function.
 function makeArray(arg1, arg2){ return [ this, arg1, arg2 ]; } 

The most common way: a global challenge


Beginners often declare functions as shown in the example above. Calling this function is not difficult:
 makeArray('one', 'two'); // => [ window, 'one', 'two' ] 

Wait a minute Where did the window object come from? Why is this window equal to us?

In JavaScript, it doesn’t matter whether the script is executed in a browser or in another environment, a global object is always defined. Any code in our script that is not “attached” to something (that is, located outside the object declaration) is actually in the context of a global object. In our case, makeArray is not just a function, “walking” by itself. In fact, makeArray is a method of a global object (in the case of execution of code in the browser) window . Proving it is easy:
 alert( typeof window.methodThatDoesntExist ); // => undefined alert( typeof window.makeArray ); // => function 

That is, the call makeArray('one', 'two'); equivalent to calling window.makeArray('one', 'two'); .
')
I am saddened by the fact that this method of calling functions is the most common, because it implies the presence of a global function. And we all know that global functions and variables are not the best way to program. This is especially true for javascript. Avoid global definitions, and you will not regret.

Function call rule # 1: If the function is called directly, without specifying an object (for example, myFunction() ), the value of this will be a global object ( window if the code is executed in the browser).

Method call


Let's create a simple object and make makeArray its method. We declare the object using literal notation, and then call our method:
 //   var arrayMaker = { someProperty: '- ', make: makeArray }; //   make() arrayMaker.make('one', 'two'); // => [ arrayMaker, 'one', 'two' ] //  ,    arrayMaker['make']('one', 'two'); // => [ arrayMaker, 'one', 'two' ] 

See the difference? The value of this in this case is the object itself. Why not window , as in the previous case, because the function declaration has not changed? The whole secret is how functions are passed to javascript. Function is a standard type of JavaScript that is actually an object, and like any other object, functions can be transferred and copied. In this case, we kind of copied the entire function, including the argument list and body, and assigned the resulting object to the make property of the arrayMaker object. This is equivalent to this ad:
 var arrayMaker = { someProperty: '- '; make: function (arg1, arg2) { return [ this, arg1, arg2]; } }; 

Function call rule # 2: In a function called using the method call syntax, for example, obj.myFunction() or obj['myFunction']() , this will have the value obj .

Failure to understand this simple principle, in general, often leads to errors in event handling:
 <input type="button" value="Button 1" id="btn1" /> <input type="button" value="Button 2" id="btn2" /> <input type="button" value="Button 3" id="btn3" onclick="buttonClicked();" /> <script type="text/javascript"> function buttonClicked(){ var text = (this === window) ? 'window' : this.id; alert( text ); } var button1 = document.getElementById('btn1'); var button2 = document.getElementById('btn2'); button1.onclick = buttonClicked; button2.onclick = function(){ buttonClicked(); }; </script> 

Clicking the first button will show the message “btn1” , because in this case we call the function as a method, and this inside the function will receive the value of the object to which this method belongs. Clicking on the second button will produce a “window” , because in this case we call buttonClicked directly (that is, not as obj.buttonClicked() ). The same thing happens when we assign an event handler to an element tag, as in the case of the third button. Clicking on the third button will display the same message as the second.

When using libraries like jQuery, you don’t need to think about it. jQuery takes care of rewriting the value of this in the event handler so that the value of this element that triggered the event:
 //  jQuery $('#btn1').click( function() { alert( this.id ); // jQuery   ,  'this'   }); 

How does jQuery manage to change the value of this ? Read below.

Two more ways: apply() and call()


It makes sense that the more often you use functions, the more often you have to pass them and call them in different contexts. Often there is a need to override the value of this . If you remember, functions in JavaScript are objects. In practice, this means that functions have predefined methods. apply() and call() are two of them. They allow you to override the value of this :
 var car = { year: 2008, model: 'Dodge Bailout' }; makeArray.apply( car, [ 'one', 'two' ] ); // => [ car, 'one', 'two' ] makeArray.call( car, 'one', 'two' ); // => [ car, 'one', 'two' ] 

These two methods are very similar. The first parameter overrides this . The differences between them are in the following arguments: Function.apply() takes an array of values ​​that will be passed to the function, and Function.call() takes the arguments separately. In practice, in my opinion, it is more convenient to apply() .

Function call rule # 3: If you want to override the value of this without copying the function to another object, you can use myFunction.apply( obj ) or myFunction.call( obj ) .

Constructors


I will not dwell on the declaration of native types in JavaScript, but I consider it necessary to remind that there are no classes in JavaScript, and any custom type needs a constructor. In addition, custom type methods are better declared through prototype , which is a property of the constructor function. Let's create our type:
 //   function ArrayMaker(arg1, arg2) { this.someProperty = ''; this.theArray = [ this, arg1, arg2 ]; } //   ArrayMaker.prototype = { someMethod: function () { alert(' someMethod'); }, getArray: function () { return this.theArray; } }; var am = new ArrayMaker( 'one', 'two' ); var other = new ArrayMaker( 'first', 'second' ); am.getArray(); // => [ am, 'one', 'two' ] 

Important in this example is the presence of the operator new before the function call. If it were not for him, it would be a global call, and the properties created in the constructor would refer to a global object. We do not need this. In addition, constructors usually do not return values ​​explicitly. Without the new operator, the constructor would return undefined , with it it returns this . A good style is the name of designers with a capital letter; This will remind you of the need for the new operator.

Otherwise, the code inside the constructor will most likely be similar to the code that you would write in another language. The value of this in this case is a new object that you are creating.

Function call rule # 4: When calling a function with the new operator, the value of this will be a new object created by the JavaScript runtime. If this function does not return any object explicitly, this will be implicitly returned.

Conclusion


I hope that understanding the difference between different ways of calling functions will make it possible for you to improve your JavaScript code. Sometimes it is not easy to catch the errors associated with the value of this , so it makes sense to prevent them from occurring in advance.

Source: https://habr.com/ru/post/119841/


All Articles