This context is in the details.

Lecture




  1. Calling a function with new
  2. Call in the context of the object
  3. Normal mode call
  4. Explicitly indicating this: apply and call
    1. call method
    2. apply method
    3. "Lending method"
    4. Making arguments real Array
    5. Call apply through apply
  5. Total

The value of this in JavaScript does not depend on the object in which the function is created. It is determined during the call .

Any function can have this in itself.

It does not matter whether it is declared in or outside the object.

The value of this is called the context of the call and will be determined at the time of the function call.

For example: this function is quite acceptable:

function sayHi() {
   alert( this .firstName );
}

This function does not yet know what this will be. This will become clear when you run the program.

There are several rules by which JavaScript sets this .

Calling a function with new

When calling a function with new , the value of this is the newly created object. We have already discussed this in the section on creating objects.

Call in the context of the object

The most common case is when a function is declared in an object or assigned to it, as in the example below:

01 var user = {
02    firstName: "Вася"
03 };
04
05 function func() {
06    alert( this .firstName );
07 }
08
09 user.sayHi = func;
10
11 user.sayHi(); // this = user

When calling a function as an object method , through a dot or square brackets, the function gets this object in this . In this case, user.sayHi() assign this = user .

If the same function is run in the context of different objects, it will receive a different this :

01 var user = { firstName: "Вася" };
02 var admin = { firstName: "Админ" };
03
04 function func() {
05    alert( this .firstName );
06 }
07
08 user.a = func; // присвоим одну функцию в свойства
09 admin.b = func; // двух разных объектов user и admin
10
11 user.a(); // Вася
12 admin[ 'b' ](); // Админ (не важно, доступ через точку или квадратные скобки)

The value of this does not depend on how the function was created, it is determined exclusively at the time of the call.

Normal mode call

If the function uses this - this implies working with the object. But a direct call to func() technically possible.

As a rule, this situation occurs when an error in development.

In doing so, this gets the value of window , a global object .

1 function func() {
2    alert( this ); // выведет [object Window] или [object global]
3 }
4
5 func();

In the modern standard of language, this behavior is changed, instead of the global object this will be undefined .

1 function func() {
2    "use strict" ;
3    alert( this ); // выведет undefined (кроме IE<10)
4 }
5
6 func();

... But by default browsers behave in the old way.

Explicitly indicating this: apply and call

The function can be called by explicitly specifying the value of this .

To do this, it has two methods: call and apply .

call method

call method syntax:

func.call(context, arg1, arg2,...)

In this case, the func function is called, the first call argument becomes its this , and the rest are passed "as is".

A call to func.call(context, a, b...) is the same as a normal call to func(a, b...) , but with an explicitly specified context .

For example, the function showName in the example below is called via call in the context of the user object:

01 var user = {
02    firstName: "Василий" ,
03    lastName: "Петров"
04 };
05
06 function showName() {
07    alert( this .firstName + ' ' + this .lastName );
08 }
09
10 showName.call(user) // "Василий Петров"

You can make it more universal by adding arguments:

01 var user = {
02    firstName: "Василий" ,
03    surname: "Петров"
04 };
05
06 function getName(a, b) {
07    alert( this [a] + ' ' + this [b] );
08 }
09
10 getName.call(user, 'firstName' , 'surname' ) // "Василий Петров"

Here, the getName function getName called with the context this = user and prints user['firstName'] and user['surname'] .

apply method

The call method rigidly records the number of arguments, separated by commas:

f.call(context, 1, 2, 3);
.. But what if we want to call a function with four arguments? But what if the number of arguments is not known in advance, and is determined at runtime?

To solve this problem, there is a method apply .

Calling a function with func.apply works like func.call , but takes an array of arguments instead of a list:

func.call(context, arg1, arg2...)
// то же что и:
func.apply(context, [arg1, arg2 ... ]);

These two lines work the same way:

getName.call(user, 'firstName' , 'surname' );
getName.apply(user, [ 'firstName' , 'surname' ]);

The apply method is much more powerful than call , since you can form an array of arguments dynamically:

1 var args = [];
2 args.push( 'firstName' );
3 args.push( 'surname' );
4
5 func.apply(user, args); // вызовет func('firstName', 'surname') c this=user

Call call/apply with null or undefined

When specifying the first argument null or undefined in call/apply , the function gets this = window :

1 function f() { alert( this ) }
2
3 f.call( null ); // window

This behavior is corrected in the modern standard (15.3).

If the function works in strict mode, then this passed "as is":

1 function f() {
2    "use strict" ;
3
4    alert( this ); // null, "как есть"
5 }
6
7 f.call( null );

"Lending method"

With call/apply you can easily take the method of one object, including the built-in one, and call it in the context of another.

In JavaScript, object methods, even embedded ones, are functions. Therefore, you can copy a function, even a built-in function, from one object to another.

This is called “method borrowing” (in English method borrowing ).

We use this technique to simplify the manipulation of arguments . As we know, this is not an array, but an ordinary object .. But I would like to call on it array methods.

1 function sayHi() {
2    arguments.join = [].join; // одолжили метод (1)
3
4    var argStr = arguments.join( ':' ); // (2)
5
6    alert(argStr); // сработает и выведет 1:2:3
7 }
8
9 sayHi(1, 2, 3);

In line (1) created an array. It has the [].join(..) method, but we do not call it, but copy it, like any other property into the arguments object. In line (2) launched it, as if he was always there.

Why does the challenge work?

Here the join method of the array is copied and called in the context of arguments . Will something bad happen because arguments are not an array? Why did he work at all?

The answer to these questions is simple. In accordance with the specification, inside the join implemented like this:

01 function join(separator) {
02    if (! this .length) return '' ;
03
04    var str = this [0];
05
06    for ( var i = 1; i< this .length; i++) {
07      str += separator + this [i];
08    }
09   
10    return str;
11 }

As you can see, this , numeric indices and the length property are used. If these properties are there, then everything is fine. And nothing more is needed. Even a regular object fits:

1 var obj = { // обычный объект с числовыми индексами и length
2    0: "А" ,
3    1: "Б" ,
4    2: "В" ,
5    length: 3
6 };
7
8 obj.join = [].join;
9 alert( obj.join( ';' ) ); // "A;Б;В"

... However, direct copying of the method is not always acceptable.

Imagine for a moment that instead of arguments , we have an arbitrary object, and we want to call the method [].join in its context. Copying this method, as we did above, is dangerous: what if an object has its own join ? We will rewrite, and then something will break ..

For a safe call, use apply/call :

01 function sayHi() {
02    var join = [].join; // ссылка на функцию теперь в переменной
03
04    // вызовем join с this=arguments,
05    // этот вызов эквивалентен arguments.join(':') из примера выше
06    var argStr = join.call(arguments, ':' );
07
08    alert(argStr); // сработает и выведет 1:2:3
09 }
10
11 sayHi(1, 2, 3);

We called the method without copying. Clean, safe.

Making arguments real Array

JavaScript has a very simple way to make a real array of arguments . To do this, take the array method: arr.slice (start, end).

According to the standard, it copies a part of the arr array from start to end into a new array. And if start and end not specified, it copies the entire array.

Call it in the context of arguments :

1 function sayHi() {
2    // вызов arr.slice() скопирует все элементы из this в новый массив
3    var args = [].slice.call(arguments);
4  
5    alert( args.join( ':' ) ); // args -- массив аргументов
6 }
7
8 sayHi(1,2);

As in the case of join , such a call is possible because slice uses only numbered properties and length from an array. All this is in the arguments .

Call apply through apply

With the help of apply we can make a universal "redirect" call from one function to another.

For example, the function f calls g in the same context, with the same arguments:

function f(a, b) {
   g.apply( this , arguments);
}

The advantage of this approach is that it is completely universal:

  • You will not need to change it if new arguments are added to f .
  • If f is an object method, then the current context will also be passed. If it isn’t, then this here seems to have nothing to do with it, but there will be no harm from it either.

Importance: 5

Write a function f that will wrap around another function g . The function f processes the first argument itself, and passes the remaining arguments to the function g no matter how many.

For example:

1 function f() { /* ваш код */ }
2
3 function g(a, b, c) {
4    alert( a + b + (c || 0) );
5 }
6
7 f( "тест" , 1, 2); // f выведет "тест", дальше g посчитает сумму "3"
8 f( "тест2" , 1, 2, 3); // f выведет "тест2", дальше g посчитает сумму "6"

The function code f should not depend on the number of arguments.

Decision

01 function f(a) {
02    alert(a);
03    var args = [].slice.call(arguments, 1);
04    g.apply( this , args);
05 }
06
07 function g(a, b, c) {
08    alert( a + b + (c || 0) );
09 }
10
11 f( "тест" , 1, 2);
12 f( "тест2" , 1, 2, 3);

[Open task in new window]

Importance: 3

Calls (1) and (2) in the example below do not work like (3) and (4) :

01 "use strict"
02
03 var obj, f;
04
05 obj = {
06    go: function () { alert( this ); } ); }
07 };
08
09 obj.go(); // (1) object
10
11 (obj.go)(); // (2) object
12
13 (f = obj.go)(); // (3) undefined
14
15 (obj.x || obj.go)(); // (4) undefined

What's the matter? Explain the logic of this operation.

Decision
  1. Normal function call in the context of the object.
  2. The same, brackets do not affect anything.
  3. Here is not just a call to obj.method() , but a more complex call of the form (выражение).method() . Such a call works as if it were split into two lines:
    f = obj.go; // сначала вычислить выражение
    f(); // потом вызвать то, что получилось
    In this case, f() is executed as a normal function, without passing this .
  4. There is also an expression to the left of the point, the call is similar to two lines.

The specification explains this with a special internal type, the Reference Type.

If more - then obj.go() consists of two operations:

  1. First get the property obj.go
  2. Then call it as a function.

But where does step 2 get this ? Just for this, the operation of obtaining the obj.go property returns the value of a special Reference Type , which in addition to the go property contains information about obj . Further, in the second step, calling it with the help of brackets () correctly sets this .

Any other operations, besides the call, turn the Reference Type into a normal type, in this case, the go function (so this type is arranged).

Therefore, it turns out that (a = obj.go) assigns to the variable a function go , already without any information about the object obj .

The situation is similar in case (4) : the OR operator || makes the Reference Type ordinary function.

[Open task in new window]

Importance: 2

What will be the result of this code?

1 var obj = {
2    go: function () { alert( this ) }
3 }
4
5 (obj.go || 0)()

PS There is a catch.

Decision
Solution Step 1

Error !

Try:

1 var obj = {
2    go: function () { alert( this ) }
3 }
4
5 (obj.go || 0)() // error!

And the error message is very strange . In most browsers, this is obj is undefined .

The point, oddly enough, is not in the declaration of obj , but in the fact that a semicolon is missing after it.

JavaScript ignores line (obj.go || ..) before parentheses (obj.go || ..) and reads this code as:

var obj = { go:... }(obj.go || 0)()

The interpreter will attempt to evaluate this expression, which denotes a call to the { go: ... } object as a function with an argument (obj.go || 0) . In this case, of course, an error will occur.

And what will happen if you add a semicolon?

1 obj = {
2    go: function () { alert( this ); } ); }
3 } ;
4
5 (obj.go || 0)();

Will everything be alright? What will be the result?

Solution Step 2

The result is a window , because calling obj.go || 0 obj.go || 0 similar to the code:

1 obj = {
2    go: function () { alert( this ); } ); }
3 };
4
5 var f = obj.go || 0; f = obj.go || 0; // эти две строки - аналог (obj.go || 0)();
6 f(); // window

[Open task in new window]

Importance: 5

What will be the result? Why?

1 arr = [ "a" , "b" ];
2
3 arr.push( function () { alert( this ); } )
4
5 arr[2](); // ?

Decision

The call arr[2]() is a call to the object method obj[method]() , the role of obj is arr , and the role of the method is: 2 .

Therefore, as is the case when calling a function as a method, the function arr[2] will get this = arr and output an array:

1 arr = [ "a" , "b" ];
2
3 arr.push( function () { alert( this ); } )
4
5 arr[2](); // "a","b",function

[Open task in new window]

Total

The value of this set depending on how the function is called:

When calling a function as a method
obj.func(...) // this = obj
obj[ "func" ](...)
With a normal call
func(...) // this = window
In new
new func() // this = {} (новый объект)
Explicit indication
func.apply(ctx, args) // this = ctx (новый объект)
func.call(ctx, arg1, arg2, ...)

Comments


To leave a comment
If you have any suggestion, idea, thanks or comment, feel free to write. We really value feedback and are glad to hear your opinion.
To reply

Scripting client side JavaScript, jqvery, BackBone

Terms: Scripting client side JavaScript, jqvery, BackBone