'this' не визначено в методах класу JavaScript


86

Я новачок у JavaScript. Нове, наскільки все, що я насправді робив з ним, - це налаштування існуючого коду та написання невеликих бітів jQuery.

Зараз я намагаюся написати "клас" з атрибутами та методами, але маю проблеми з методами. Мій код:

function Request(destination, stay_open) {
    this.state = "ready";
    this.xhr = null;
    this.destination = destination;
    this.stay_open = stay_open;

    this.open = function(data) {
        this.xhr = $.ajax({
            url: destination,
            success: this.handle_response,
            error: this.handle_failure,
            timeout: 100000000,
            data: data,
            dataType: 'json',
        });
    };

    /* snip... */

}

Request.prototype.start = function() {
    if( this.stay_open == true ) {
        this.open({msg: 'listen'});
    } else {

    }
};
//all console.log's omitted

Проблема, в Request.prototype.start, thisневизначена, і, отже, оператор if має значення false. Що я тут роблю не так?


Чи є причина , у вас є startв prototype?
— xj9

Що Request.prototypeвстановлено?
— Matt Ball

У мене було подібне запитання тут: stackoverflow.com/questions/3198264/…, в якому є купа корисних посилань. Суть цього полягає thisв тому, що в JavaScript немає постійного посилання на "власника" прототипової функції, що викликається, як це було б у більшості мов OO, таких як Java.
— Marc Bollinger

1
@Matt: Запит - це функція конструктора. Request.prototype за замовчуванням new Object(). Все, що ви додаєте до нього, автоматично стає властивістю об’єктів, створених за допомогою new Request().
— Chetan S

@Matt Ball Request.prototype- це місце, де є випадки Requestуспадкування від. У цьому випадку це, мабуть, Functionабо Object.
— xj9

Відповіді:


68

Як ви викликаєте функцію запуску?

Це має спрацювати ( ключовим є нове )

var o = new Request(destination, stay_open);
o.start();

Якщо ви прямо назвете це як Request.prototype.start(), thisпосилатиметься на глобальний контекст (window у браузерах).

Крім того, якщо thisне визначено, це призводить до помилки. Вираз if не має значення false.

Оновлення : thisоб’єкт встановлюється не на основі декларації, а шляхом виклику . Це означає, що якщо ви призначите властивість функції змінній like x = o.startі call x(), thisвсередині start більше не посилається o. Це те, що відбувається, коли ти робиш setTimeout. Щоб це працювало, зробіть замість цього:

 var o = new Request(...);
 setTimeout(function() { o.start(); }, 1000);

Я використовую setTimeout:var listen = new Request(destination, stay_open); setTimeout(listen.start, 500);
— Carson Myers

Це врятувало мені життя, намагаючись зрозуміти, чому функція, яку я передавав для вираження ' basicAuth, не працює з однаковим результатом.
— Едісон Спенсер

Або зробити o.start.bind(o). Чому не x = o.start; x()працює?
— theonlygusti

.bind()повертає нову функцію, яка не працює на місці. Тож вам доведеться зробити x = o.start.bind(o); x()абоo.start = o.start.bind(o); x=o.start; x()
— ceedob

39

Я просто хотів зазначити, що іноді ця помилка трапляється, оскільки функція була використана як функція вищого порядку (передана як аргумент), а потім область thisвтратилася. У таких випадках я б рекомендував передавати таку функцію, прив'язану до this. Напр

this.myFunction.bind(this);

3
Гарне пояснення !! Це було саме те, що я шукав.
— vandersondf

1
не можна сказати, скільки головного болю ти мене врятував
— Native Coder

Чому сфера thisвтрачається?
— theonlygusti

17

ООП JavaScript трохи забавний (або багато), і потрібно трохи звикнути. Перше, про що потрібно пам’ятати, - це те, що занять не існує, і мислення в термінах занять може вас спокусити. І для того, щоб використовувати метод, приєднаний до конструктора (еквівалент JavaScript визначення класу), вам потрібно створити екземпляр вашого об'єкта. Наприклад:

Ninja = function (name) {
    this.name = name;
};
aNinja = new Ninja('foxy');
aNinja.name; //-> 'foxy'

enemyNinja = new Ninja('boggis');
enemyNinja.name; //=> 'boggis'

Зверніть увагу, що Ninjaекземпляри мають однакові властивості, але aNinjaне можуть отримати доступ до властивостей enemyNinja. (Ця частина повинна бути дуже простою / простою). Коли ви починаєте додавати матеріали до prototype:

Ninja.prototype.jump = function () {
   return this.name + ' jumped!';
};
Ninja.prototype.jump(); //-> Error.
aNinja.jump(); //-> 'foxy jumped!'
enemyNinja.jump(); //-> 'boggis jumped!'

Виклик цього безпосередньо призведе до помилки, оскільки thisлише вказує на правильний об'єкт (ваш "Клас"), коли конструктор створюється за допомогою екземпляра (інакше він вказує на глобальний об'єкт windowу браузері)


6

У ES2015, також відомий як ES6, classє синтаксичним цукром для functions.

Якщо ви хочете змусити встановити контекст, thisви можете використовувати bind()метод. Як зазначив @chetan, під час виклику ви також можете встановити контекст! Перевірте приклад нижче:

class Form extends React.Component {
constructor() {
    super();
  }
  handleChange(e) {
    switch (e.target.id) {
      case 'owner':
        this.setState({owner: e.target.value});
        break;
      default:
    }
  }
  render() {
    return (
      <form onSubmit={this.handleNewCodeBlock}>
        <p>Owner:</p> <input onChange={this.handleChange.bind(this)} />
      </form>
    );
  }
}

Тут ми змушені контекст всередині handleChange()до Form.


6
Вам слід прив'язати функцію до цього в конструкторі. В іншому випадку ви прив'язуєте його кожен renderраз, коли викликається замість одного разу, коли клас створюється. Крім того, більша частина вашого прикладу насправді не відповідає питанню.
— erich2k8,

Або використовуйте синтаксис стрілки при визначенніhandleChange()
— Нітін

0

На це запитання отримано відповідь, але, можливо, це може бути хтось інший, хто приходить сюди.

У мене також була проблема, де thisневизначено, коли я безглуздо намагався деструктурувати методи класу при його ініціалізації:

import MyClass from "./myClass"

// 'this' is not defined here:
const { aMethod } = new MyClass()
aMethod() // error: 'this' is not defined

// So instead, init as you would normally:
const myClass = new MyClass()
myClass.aMethod() // OK


0

Використовуйте функцію стрілки:

Request.prototype.start = () => {
    if( this.stay_open == true ) {
        this.open({msg: 'listen'});
    } else {

    }
};
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.