инициализация свойств в JavaScript [закрыт]

Я создал класс В JavaScript следующим образом:

var Test = function(){
    this.element = null;
    this.init = function() {
        if(Test.html == "") {
            Test.loadHtml(this);
            return;
        }
        this.initElements();
        this.someMethodInternalCall();
    };
    this.initElements = function() {
        // append the loaded html to body
        // etc...
        this.element = $("some-element-contained-in-loaded-html-apended-to-body");
    }
    this.someMethodInternalCall = function() {
        this.element.css({some:style}); // works in this place
    }
    this.someMethodExternalCall = function() {
        this.element.css({some:style}); // dosn't work in this place
        // I mean here this.element is null. WHY?
    }
};
Test.html = "";
Test.loadHtml = function() {
    // load html content by an ajax request (jQuery $.ajax method)
    // and put it in Test.html
    // then recall the Test.init ethod
    return function(caller) {
        Test.html = // load html by ajax... etc...
        caller.init();
    };
}();

function someUsage(){
    var t = new Test();
    t.init();
    t.element.css({some:style}); // error: t.element is null WHY?
    t.someMethodExternalCall(); // error: this.element is null WHY?
}

как вы можете видеть, я объясняю в коде выше. Почему, когда мы устанавливаем свойства после их инициализации, это просто влияет на внутренние вызовы? Как я могу создать свойство, которое я могу изменить его значение?

обновление:

кажется, я должен объяснить свой код. В задание Все о element собственность, а не Test.html, ни Test.loadHtml метод или вызов его. Test.loadHtml уволят currectly (вы можете проверить его) и Test.html получает загруженный html, а загруженный html добавляется в body и так далее. Это шаблон JavaScript (я забыл, как его зовут) и работает корректно. Единственное, что неправильно, это инициализация свойства -element.

2 ответов


проблема asynchronicity. Когда вы собираетесь загрузить HTML через AJAX, остальная часть функции продолжается...

function someUsage(){
  var t = new Test();
  t.init();
  // The following carries on whilst the request is loading it does not wait
  t.element.css({some:style}); // This is why I am null
  t.someMethodExternalCall(); // This is why I am also null
}

чтобы обойти это, вы можете использовать обратный вызов...

function someUsage(){
  var t = new Test();
  t.init(function() {
    // I do not continue until the loadHtml request has completed
    t.element.css({some:style}); // I am not null anymore
    t.someMethodExternalCall(); // I am not null anymore
  });
}

вам нужно будет изменить функцию init и функцию loadHtml для вызова обратного вызова, а не метода init вызывающего объекта, функции init...

this.init = function(callback) {

  // Using blank Test.html to determine whether the html has been loaded
  if(Test.html == "") {
    var me = this;

    // Call loadHtml with a callback function
    Text.loadHtml(function() {

      // I want to keep the this reference to the object and callback argument
      me.init(callback);
    });

  // It is loaded so continue set up and then trigger the callback
  } else {
    this.initElements();
    this.someMethodInternalCall();
    callback();
  }
};

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

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

Test.loadHtml = function(callback) {

  // If already loading roll up callbacks
  if(Test.loading) {

    // Callback becomes a function that calls the original callback function 
    // and then the new one
    Test.callback = (function(original) {
      return function() {
        original();
        callback();
      }
    }) (Test.callback);

  // First time it has been called set the flag to prevent multiple loads 
  // and add the callback
  } else {
    Test.loading = true;
    Test.callback = callback;

    // Added to illustrate the AJAX callback functionality
    ajax("html", function(response) {
      Test.html = response;
      Test.callback();
    });
  }
}();

предпочтительным подходом является обеспечение действительности объекта во время создания экземпляра, это предотвращает эти условия гонки. Возникает ошибка, если класс не может быть создан достоверно, это перемещает сложность, окружающая порядок ваших операций из класса. Как вы можете видеть ниже, это не так красиво, и вы должны позвонить на этапе загрузки (или что-то еще вызвать его).

new Test(); // Errors not loaded!
// We must perform the load step to use the class
Test.load(function() {
  new Test(); // Works!
});

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

// views is some object managing the loading of view classes when asked for
// one or more views it will load the required HTML and return the class(es)
// so we can create instances...
views.load("Test", function(Test) {
  var t = new Test();
  t.element.css({some: style});
  t.someMethodExternalCall();
});

ты делаешь caller.init(); в обратном вызове функции ajax в loadHtml?

если нет, ваша функция init будет добавлена в стек выполнения до загрузки html (и именно поэтому this.element равно null)

Test.loadHtml = function() {
    // load html content by an ajax request (jQuery $.ajax method)
    // and put it in Test.html
    // then recall the Test.init ethod
    return function(caller) {
      $.ajax({
        url: 'somethinghere',
        success: function(data) {
            Test.html = data;
            caller.init();
        }
      });
    };