7

次のように 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ん。正しく起動され(テストできます)、ロードされたhtmlを取得し、ロードされたhtmlが追加されます。これは JavaScript パターン (名前は忘れました) であり、正しく動作します。唯一間違っているのは、プロパティの初期化についてです - 。Test.loadHtmlTest.loadHtmlTest.htmlbodyelement

4

2 に答える 2

7

問題は非同期性です。AJAX 経由で HTML を読み込もうとすると、残りの機能が続行されます...

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 メソッドである init 関数ではなく、コールバックを呼び出すように init 関数と loadHtml 関数を変更する必要があります...

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();
  }
};

これらの Test クラスを多数作成すると、他のクラスがロードされている間にそれぞれが 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();
});
于 2012-12-31T14:28:10.233 に答える
2

caller.init();のajax関数のコールバックでやってますloadHtmlか?

そうでない場合、html がロードされる前に init 関数が実行スタックに追加されます (それ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();
        }
      });
    };
于 2012-12-31T14:33:44.860 に答える