javascript – Call context loss

Question:

Can you please explain why after assigning var f = obj1.f the call context is lost and undefined is displayed?

var obj1 = {

  x: 3,

  f: function() {
    return (this.x);
  }
};

alert(obj1.f());
var f = obj1.f;
alert(f());

Answer:

The value of this inside a function depends on how the function is called and how the function is created.

How is it called?

You can call a function in the following ways:

Function call

If there is a regular function, in most cases the value of this will be the global object (for the browser's window ). When using "strict mode"undefined .

var f = function (){
  console.log('common:',this.toString());
};
f();

var fStrict = function (){
  "use strict";
  console.log('strict:', this);
};
fStrict();

This is usually how callback functions are called, which is why the this value in them seems unexpected.

For example, when passing a function to .addEventListener , the this value will be the element to which the handler is added.


Method call

A method is a function in an object.

var Obj = {toString:function(){ return "[object Obj]";}};
Obj.f = function (){
  console.log('common:',this.toString());
};
Obj.f();

Obj.fStrict = function (){
  "use strict";
  console.log('strict:', this.toString());
};
Obj.fStrict();

When a function is called as a method, the this value is the object in which the function resides, in fact the value before the dot character.


Constructor call

A function can be called as a constructor by using the new operator before the call: new Foo()

function Foo(name){
  this.name = name;
}

var foo = new Foo('foo');
console.log(foo);

When a function is called as a constructor, a new object is created, and the this value refers to this created object.

Feature: when using inheritance and classes from ES2015 , accessing this before calling super , depending on the browser, will throw an exception about trying to access an undeclared / uninitialized variable.

class A {}
class B extends A {
  constructor(){
    console.log(this);
  }
}
var b = new B();

Calling with the call and apply methods

When using the call and apply functions, you can set the this value directly by passing it as the first parameter.

var f = function (){
  console.log('common:',this);
};
f.call({o:'object'});

var fStrict = function (){
  "use strict";
  console.log('strict:', this);
};
fStrict.apply({o:'object'});

In libraries like jQuery , callbacks passed to various functions, such as each , map , on , and others, are called using these functions. In this case, the current element of the collection, or an html element, is set as this.


Calling as Callbacks in Array Processing Functions

Some built-in functions for an Array object also allow you to directly specify the this value for the passed callback:

var specialMap = {'b':'specialB','d':'specialD'}
var source= ['a','b','c','d','e'];
var mapped = source.map(function(el){
  return this[el] || ('common-'+el);
},specialMap);

console.log('source:',source);
console.log('mapped:',mapped);

How is it created?

Declaring a function or function expression

Normal function declaration:

function A(){}

var a = function (){};

in a normal declaration, the this value is determined when called in the ways described above.


Creating a function with bind

The bind function returns a new bound function. The this value inside the created function is always the one passed when bind was called.

An important feature: when using a bound function as a constructor, the this value will still point to the object being created, as described above.

An important feature: in NOT strict mode , when passing null and undefined as the this parameter, this parameter will be ignored and this will be set to the global object.

function A(){console.log(typeof this,'is window', this === window);}
console.log('execute with null');
A.bind(null)();
console.log('execute with undefined');
A.bind(undefined)();

function A1(){'use strict'; console.log(typeof this, this);}
console.log('execute with null');
A1.bind(null)();
console.log('execute with undefined');
A1.bind(undefined)();

An important feature: the this value of the created function cannot be overridden using the call and apply functions described above.

function A(){console.log(this);}

var B = A.bind({o:'object'});
console.log('execute binded');
B();
console.log('execute with call');
B.call({another: 'some new object'});

console.log('execute as constructor');
new B();

Arrow functions

Arrow functions were introduced in ES2015 and are bound to the current value of this when they are created.

Once created, the this value cannot be changed in the ways above.

Also, an arrow function cannot be used as a constructor.

function A(){
  this.t = (place)=>console.log(place,this);
}  

var a = new A()
a.t('method:');

var tt = a.t;
tt('free function execute:');

tt.call({o:'object'},'using call function');

new tt('constructor');

на основе ответов:
How does the “this” keyword work?
How does “this” keyword work within a JavaScript object literal?

Scroll to Top