Это "это" определенно сбивает с толку...

ключевое слово "это"

Когда объект javascript выполняет действие, используя свой метод и свойства, он должен иметь возможность читать свое собственное свойство. this — это ключевое слово в javascript, которое позволяет объекту ссылаться на собственное свойство.

const circle = {
  // property: state of object
  radius: 5,
  // method: an action that read and update state.
  getDiameter() {
    // When this method wants to refer other property or methods of its own
    // it should be able to refer circle object.
    return 2 * circle.radius;
  }
};

Литерал объекта может иметь возможность ссылаться на себя рекурсивным способом вызова самого себя, но это не рекомендуется, поскольку объект может не быть выделен в памяти до вызова метода.

function Circle(radius) {
  // we never know whom we are referring to.
  ???.radius = radius;
};

Circle.prototype.getDiameter = function () {
// we never know whom we are referring to.
  return 2 * ???.radius;
};

// we must define constructor function before we create instance using it.
const circle = new Circle(5);

В отличие от литерала объекта, создание объекта с помощью функции конструктора требует, чтобы объект ссылался рекурсивно, но функция конструктора требует, чтобы объект ссылался.

Ой, там есть проблема, здесь «это» играет важную роль.

«Это» — это самоссылающаяся переменная, которая указывает на существующий объект или экземпляр, который она собирается создать. «это» создается движком javascript, и на него можно ссылаться в любом месте кода. Однако эта привязка (экземпляр, на который ссылается this) определяется динамически.

Способы вызова функции и эта привязка

Эта привязка определяется динамически в зависимости от того, как функция вызывается в javascript.

  1. Обычная функция
  2. Метод Функция
  3. Функция конструктора
  4. Косвенные способы, такие как Function.prototype.apply/call/bind
// this will be different depending on how its called.
const foo = function () {
 console.dir(this);
}

// 1. regular function
// call foo function in a common way.
// this in foo refers to a global object window.
foo(); // window

// 2. method
// assign foo as a property of obj
// this in foo refers to an object obj where method is called.
const obj = { foo };
obj.foo(); // obj

// 3. constructor function
// call foo with new operator as constructor function.
// this in foo refers to an instance the constructor function created.
new foo(); // foo {}

// 4. Indirect ways such as function.prototype.apply/call/bind
// this in foo is decided by its argument.
const bar = {name: 'bar'};

foo.call(bar); // bar
foo.apply(bar); // bar
foo.bind(bar)(); // bar

Обычная функция

Все «это» в функции Regular привязывается к глобальному объекту.

//Nested Regular Function
function foo() {
  console.log("foo's this: ", this) // window
  function bar() {
    console.log("bar's this: ", this) // window
  }
}

foo();

// Regular Function in Method Function
var value = 1;
const obj = {
  value: 100,
  foo() {
    console.log("foo's this: ", this) // {value:100, foo: f}
    console.log("foo's this.value: ", this.value) // 100
    
    // Nested function inside method
    function bar() {
      console.log("bar's this: ", this) // window
      console.log("bar's this.value: ", this.value) // 1
    }
    bar();
  }  
}

obj.foo();

// Callback function
const obj = {
  value: 100,
  foo() {
    console.log("foo's this: ", this) // {value:100, foo: f}

    // Callback function
    setTimeout(function () {
      console.log("callback's this: ", this) // window
      console.log("callback's this.value: ", this.value) // 1
    }, 100);
  }  
}

obj.foo();

Значение var объявлено как свойство окна глобального объекта и вызывается this.value в обычных функциях как 1 вместо 100.

Метод Функция

«this» в функции метода привязывается к объекту, который ее вызывает. Это означает, что this не привязывается к объекту, у которого есть метод, а привязывается к объекту, который вызывает метод.

const person = {
  name: 'Lee',
  getName() {
    return this.name;
  }
}
console.log(person.getName()); // Lee

const anotherPerson = {
  name: 'Kim'
};

// allocate getName method to anotherPerson object.
anotherPerson.getName = person.getName;

//getName is called by object of anotherPerson.
console.log(anotherPerson.getName()); // kim

// allocate getName method to a variable
const getName = person.getName;

// getName is called as a regular function.
console.log(getName()); // '' 

Функция конструктора

«Это» внутри функции конструктора будет привязано к экземпляру, который будет создан.

function Circle(radius) {
  this.radius = radius;
  this.getDiameter = function () {
    return 2 * this.radius;
  };
}

// create circle object with radius of 5
const circle1 = new Circle(5);

// create circle object with radius of 10
const circle2 = new Circle(10);

console.log(circle1.getDiameter()); // 10
console.log(circle2.getDiameter()); // 20

Функция-конструктор — это функция, которая создает новый объект. Мы определяем функцию-конструктор так же, как и обычную функцию, и когда мы вызываем эту функцию с оператором «новый», она запускается как функция-конструктор или будет работать как обычная функция.

// when function is not called with new operator, it is regular function.
const circle3 = Circle(15);

// Circle does not have return value so it returns undefined.
console.log(circle3); // undefined

// this in regular function bind to global object.
console.log(radius); // 15

Косвенные способы, такие как Function.prototype.apply/call/bind

методы применения, вызова и связывания являются методами Function.prototype, поэтому все функции в javascript могут их использовать. Мы можем передать «этот аргумент», который мы хотим, чтобы функция имела, как «это».

// apply receives this argument and an argument array as function argument
Function.prototype.apply(thisArg, [arg1, arg2, arg3...])

//call receives this argument and arguments seperated by commas.
Function.prototype.call(thisArg, arg1, arg2, arg3...)

//Example code
function getThisBinding() {
  return this;
}
// object to use as this
const thisArg = {a: 1};

console.log(getThisBinding.apply(thisArg)); // {a: 1}
console.log(getThisBinding.call(thisArg)); // {a: 1}

Принимая во внимание, что bind не вызывает функцию, а возвращает функцию с указанием, что эта привязка завершена.

function getThisBinding() {
  return this;
}

// object to use as this
const thisArg = {a: 1};

// bind method pass its first Argument thisArg to change this of function
// and return new getThisBinding function.
console.log(getThisBinding.bind(thisArg)); // getThisBinding

// bind method does not call the new function so we have to call it.
console.log(getThisBinding.bind(thisArg)()); // {a: 1}

Bind полезен, когда мы хотим решить ситуацию, когда this в методе и this во вложенной или callback-функции в методе не соответствуют друг другу.

const person = {
  name: 'Lee',
  foo(callback) {
    // (1)
    setTimeout(callback, 100);
  }
}

person.foo(function() {
  console.log(`Hi! my name is ${this.name}.`); // (2) Hi! my name is .
});
// global this.name is window.name in browser and default value is ''
// global this.name is undefined in Node.js

В ситуации (1) это будет относиться к объекту person, который вызывает метод foo. Однако, когда функция обратного вызова person.foo вызывается в ситуации (2), это относится к глобальному объекту window, который называется window.name.

Функция обратного вызова в person.foo является вспомогательной функцией person.foo, поэтому эти «это» должны быть одинаковыми, иначе код может работать не так, как предполагалось. К счастью, Bind может решить эту проблему.

const person = {
  name: 'Lee',
  foo(callback) {
    // this binding is passed into callback function using bind.
    setTimeout(callback.bind(this), 100);
  }
}

person.foo(function() {
  console.log(`Hi! my name is ${this.name}.`); // Hi! my name is Lee.
});

В заключение, эта привязка определяется тем, как функция вызывается следующими способами.

  1. Обычная функция — глобальный объект
  2. Функция метода — Объект, который вызывает метод
  3. Функция конструктора — Экземпляр, который будут создавать функции конструктора.
  4. Function.prototype.apply/call/bind — Первый аргумент, переданный в команду apply/call/bind.

Следующее содержание — это то, что я узнал из корейской книги Javascript Deep Dive. Комментарии по исправлению любой неправильной интерпретации приветствуются