Что Делать, Если «Это» Теряет Контекстную Ссылку

Привет, Хабр! Представляю вашему вниманию перевод статьи «Что делать, когда «это» теряет контекст» автор Кристи Салческу .

Лучший способ не потерять контекст этот - не использовать этот .

Однако, это не всегда возможно.

Например, мы работаем с чужим кодом или библиотекой, использующей этот .

Литерал объекта, функция-конструктор, конструктор объектов класса в системе прототипов.

Псевдопараметр этот используется в системе прототипирования для предоставления доступа к свойствам объекта.

Давайте рассмотрим несколько случаев.



Вложенные функции

этот теряет ссылку на контекст внутри вложенных функций.

  
  
  
  
  
  
  
  
  
  
  
  
   

class Service { constructor(){ this.numbers = [1,2,3]; this.token = "token"; } doSomething(){ setTimeout(function doAnotherThing(){ this.numbers.forEach(function log(number){ //Cannot read property 'forEach' of undefined console.log(number); console.log(this.token); }); }, 100); } } let service = new Service(); service.doSomething();

Метод сделай что-нибудь() две вложенные функции: сделатьДругое() И бревно() .

При звонке сервис.

doSomething() , этот теряет ссылку на контекст во вложенной функции.



связывать()

Одним из способов решения проблемы является метод связывать() .

Взгляните на следующий код:

doSomething(){ setTimeout(function doAnotherThing(){ this.numbers.forEach(function log(number){ console.log(number); console.log(this.token); }.

bind(this)); }.

bind(this), 100); }

связывать() создает новую версию функции, которая при вызове уже имеет определенное значение этот .

функция doAnotherThing(){ /*…*/}.

bind(this) создает версию функции сделатьДругуюВещь() , который принимает значение этот от сделай что-нибудь() .



это/я

Другой вариант — объявить и использовать новую переменную.

это/я , который будет хранить значение этот из метода сделай что-нибудь() .



doSomething(){ let that = this; setTimeout(function doAnotherThing(){ that.numbers.forEach(function log(number){ console.log(number); console.log(that.token); }); }, 100); }

Мы должны объявить пусть это = это во всех методах с использованием этот во вложенных функциях.



Стрелочные функции

Стрелочная функция дает нам еще один способ решить эту проблему.



doSomething(){ setTimeout(() => { this.numbers.forEach(number => { console.log(number); console.log(this.token); }); }, 100); }

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

Недостаток этого метода в том, что мы не можем указать имя стрелочной функции.

Имя функции важно, поскольку оно повышает читаемость кода и описывает ее назначение.

Ниже приведен тот же код с функцией, выраженной через имя переменной:

doSomething(){ let log = number => { console.log(number); console.log(this.token); } let doAnotherThing = () => { this.numbers.forEach(log); } setTimeout(doAnotherThing, 100); }



Метод как обратный вызов

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

Давайте посмотрим на следующий класс:

class Service { constructor(){ this.token = "token"; } doSomething(){ console.log(this.token);//undefined } } let service = new Service();

Давайте рассмотрим ситуации, в которых метод сервис.

doSomething() используется как функция обратного вызова.



//callback on DOM event $("#btn").

click(service.doSomething); //callback for timer setTimeout(service.doSomething, 0); //callback for custom function run(service.doSomething); function run(fn){ fn(); }

Во всех случаях выше этот теряет связь с контекстом.



связывать()

Мы можем использовать связывать() Для решения этой проблемы.

Ниже приведен код этой опции:

//callback on DOM event $("#btn").

click(service.doSomething.bind(service)); //callback for timer setTimeout(service.doSomething.bind(service), 0); //callback for custom function run(service.doSomething.bind(service));



Стрелочная функция

Другой способ — создать стрелочную функцию, которая вызывает сервис.

doSomething() .



//callback on DOM event $("#btn").

click(() => service.doSomething()); //callback for timer setTimeout(() => service.doSomething(), 0); //callback for custom function run(() => service.doSomething());



Реагировать на компоненты

В компонентах этот теряет ссылку на контекст, когда методы используются в качестве обратных вызовов для событий.



class TodoAddForm extends React.Component { constructor(){ super(); this.todos = []; } componentWillMount() { this.setState({desc: ""}); } add(){ let todo = {desc: this.state.desc}; //Cannot read property 'state' of undefined this.todos.push(todo); } handleChange(event) { //Cannot read property 'setState' of undefined this.setState({desc: event.target.value}); } render() { return <form> <input onChange={this.handleChange} value={this.state.desc} type="text"/> <button onClick={this.add} type="button">Save</button> </form>; } } ReactDOM.render( <TodoAddForm />, document.getElementById('root'));

В качестве решения мы можем создать в конструкторе новые функции, которые будут использовать связать (это) .



constructor(){ super(); this.todos = []; this.handleChange = this.handleChange.bind(this); this.add = this.add.bind(this); }



Не используйте «это»

Нет этот - нет проблем с потерей контекста.

Объекты можно создавать с помощью заводские функции .

Посмотрите на этот пример:

function Service() { let numbers = [1,2,3]; let token = "token"; function doSomething(){ setTimeout(function doAnotherThing(){ numbers.forEach(function log(number){ console.log(number); console.log(token); }); }, 100); } return Object.freeze({ doSomething }); }

Контекст сохраняется, если вы используете метод в качестве обратного вызова.



let service = Service(); service.doSomething(); //callback on DOM event $("#btn").

click(service.doSomething); //callback for timer setTimeout(service.doSomething, 0); //callback for custom function run(service.doSomething);



Заключение

этот теряет связь с контекстом в различных ситуациях.

связывать() , использование переменных это/я а стрелочные функции — это способы решения проблем с контекстом.

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

Теги: #JavaScript #context #this[]

Вместе с данным постом часто просматривают: