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

Я пытаюсь обернуть голову вокруг синтаксиса для классов в ES6. В то же время обучение Ткань уроженец через Бонни Эйзенмана Изучение Реагирующей Нации.

Я столкнулся с проблемой доступа к this в обратном вызове, когда этот обратный вызов является методом класса. Я знаю, что проблемы вокруг lexical this в обратных вызовах многократно возникали в StackOverflow. Например, в Как получить доступ к правильному контексту` this` внутри обратного вызова?.

Основываясь на моем исследовании онлайн, я нашел решение. Но я не уверен, что это правильный способ сделать это в ES6.

Моя проблема возникла, когда я попробовал следующее:

class WeatherProject extends Component {
  constructor(props) {
    super(props);
    this.state = {
      zip: ''
    };
  }

  _handleTextChange(event) {
    console.log(event.nativeEvent.text);
    this.setState({zip: event.nativeEvent.text})
  }

  render() {
    return (
      <TextInput
        style={styles.input}
        onSubmitEditing={this._handleTextChange}/>
    );
  }
}

(Которое я только немного изменил из примера в книге, чтобы соответствовать синтаксису класса ES6 и синтаксису import/export вместо Require.)

Если я это сделаю, this в _handleTextChange будет undefined (не может прочитать свойство 'setState' из undefined). Я был удивлен этим. Исходя из других языков OO, я интерпретирую, как будто этот метод ведет себя больше, как если бы это был статический метод.

Я смог решить это, пропустив метод класса и используя обозначение стрелки. onSubmitEditing={event => this.setState({name: event.nativeEvent.text})}. Что хорошо работает. У меня нет проблем или смущения.

Я действительно хочу разобраться, как вызвать метод класса. После честного исследования мне удалось сделать это, выполнив следующие действия: onSubmitEditing={this._handleTextChange.bind(this)}. Возможно, я неправильно понял фундаментальный аспект JavaScript (я новичок в JS), но для меня это кажется совершенно безумным. Нет ли способа доступа к контексту объекта из одного метода без явной привязки объекта обратно к... его собственному методу в том месте, где он вызывается?

Я также попытался добавить var self = this; в конструктор и вызвать self.setState в _handleTextChange. Но не удивился, обнаружив, что это не сработало.

Каков правильный способ доступа к объекту из одного из его методов, когда он вызывается как обратный вызов?

Ответ 1

Способ React.createClass(ES5) для создания класса имеет встроенную функцию, которая автоматически привязывает все методы к this. Но при введении classes в ES6 и миграции React.createClass, они обнаружили, что это может быть немного запутанным для разработчиков JavaScript, которые не используются для этой функции в других классах, или это может сбивать с толку, когда они переходят из React в другие классы.

Итак, они решили не иметь этого встроенного в модель класса React. Вы все еще можете явно использовать методы prebind в своем конструкторе, если хотите, чтобы

class WeatherProject extends Component {
  constructor(props) {
    super(props);
    this.state = {
      zip: ''
    };
    this._handleTextChange = this._handleTextChange.bind(this); //Binding to `this`
  }

  _handleTextChange(event) {
    console.log(event.nativeEvent.text);
    this.setState({zip: event.nativeEvent.text})
  }

Но у нас всегда есть простой способ избежать этой предобработки. Да! Ты понял. Функции стрелок.

class WeatherProject extends Component {
  constructor(props) {
    super(props);
    this.state = {
      zip: ''
    };
  }

  _handleTextChange = event => {
    console.log(event.nativeEvent.text);
    this.setState({zip: event.nativeEvent.text})
  }

  render() {
    return (
      <TextInput
        style={styles.input}
        onSubmitEditing={this._handleTextChange}/>
    );
  }
}

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

class bindTesting {
  constructor() {
    this.demo = 'Check 1';
  }

  someMethod() {
    console.log(this.demo);
  }

  callMe() {
    this.someMethod();
  }
}

let x = new bindTesting();
x.callMe(); //Prints 'Check 1';

Но это не выводит "Check 1", если мы будем называть его в выражении JSX.

EDIT:: Как упоминалось в @Oka, функции стрелок в телах классов являются функциями ES7 + и доступны в Compiler/polyfills, таких как babel. Если вы не используете транспилятор, который поддерживает эту функцию, мы можем просто привязать к this, как упоминалось выше, или написать новый базовый компонент (это плохая идея)

class BaseComponent extends React.Component {
 _bind(...methods) {
  methods.forEach( (method) => this[method] = this[method].bind(this) );
 }
}

class ExampleComponent extends BaseComponent {
 constructor() {
  super();
  this._bind('_handleTextChange', '_handleClick');
 }
 // ...
}

Ответ 2

Прерываясь от ES6 и реагируя на мгновение, в обычном старом JavaScript, когда вы передаете метод объекта вокруг него, это просто ссылка на саму функцию, а не на объект и функцию.

Любая вызывающая функция может использовать неявный this, вызывая функцию обычно или может даже выбрать изменение контекста с помощью .call и .apply, или .bind.

var O = {
  foo: function () {
    console.log(this);
  },
  bar: 51
};

O.foo(); // `this` is O, the implicit context, inferred from the object host

var foo = O.foo;

foo(); // `this` is is the global object, or undefined in Strict Mode

Ответ 3

Возможно, я неправильно понял фундаментальный аспект JavaScript (я новичок в JS), но мне это кажется совершенно безумным. Нет ли способа доступа к контексту объекта из одного метода без явной привязки объекта обратно к... его собственному методу в том месте, где он вызывается?

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

Связывание this value происходит, когда вызывается функция, в зависимости от того, как вызывается функция. Функциональный вызов с нотной записью метода имеет свой this, установленный для принимающего объекта, вызов прослушивателя событий имеет свой this, установленный для текущего целевого объекта, а обычный вызов функции имеет только undefined.

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

Если вы хотите вызвать функцию как метод в экземпляре, когда она является прослушивателем событий, вам нужно явно создать функцию, которая делает это - метод "bound". В ES6 обозначение стрелки обычно предпочтительнее для этого.