'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.