JavaScript 'класс' и одноэлементные проблемы

У меня есть одноэлементный объект, которые используют другой объект (не одиночный элемент), для требования некоторой информации к серверу:

var singleton = (function(){

  /*_private properties*/
  var myRequestManager = new RequestManager(params,
    //callbacks
    function(){
        previewRender(response);
    },
    function(){
        previewError();
    }
  );

  /*_public methods*/
  return{

    /*make a request*/
    previewRequest: function(request){
       myRequestManager.require(request);  //err:myRequestManager.require is not a func
    },

    previewRender: function(response){
      //do something
    },

    previewError: function(){
      //manage error
    }
  };
}());

Это - 'класс', которые выполняют запрос к серверу

function RequestManager(params, success, error){
  //create an ajax manager
  this.param = params;
  this._success = success;  //callbacks
  this._error = error;
}

RequestManager.prototype = {

  require: function(text){
    //make an ajax request
  },
  otherFunc: function(){
     //do other things
  }

}

Проблема состоит в том, что я не могу назвать myRequestManager.require из одноэлементного объекта. Консоль Firebug говорит: "myRequestManager.require не является функцией", но я не понимаю, где проблема. Существует ли лучшее решение для реализации эта ситуация?

9
задан Manuel Bitto 13 April 2015 в 12:18
поделиться

1 ответ

Ваш код находится в том порядке, в котором вы его цитировали, не так ли? Синглтон появляется над RequestManager в источнике?

Если да, то это ваша проблема. Это довольно тонко (!), Но если предположить, что ваши два фрагмента цитируемого кода находятся в том порядке, в котором вы их показали, вот порядок, в котором все происходит (я объясню это подробнее ниже):

  1. Функция RequestManager определен.
  2. Ваша анонимная функция, которая создает ваш синглтон, запускается, включая создание экземпляра RequestManager .
  3. Прототип RequestManager заменен новым.

Поскольку экземпляр myRequestManager был создан до , прототип был изменен, он не имеет функций, которые вы определили для этого (нового) прототипа. Он продолжает использовать объект-прототип, который был на месте, когда он был создан.

Вы можете легко исправить это, изменив порядок кода или добавив свойства к прототипу RequestManager , а не заменяя его, например:

RequestManager.prototype.require = function(text){
    //make an ajax request
};
RequestManager.prototype.otherFunc = function(){
    //do other things
};

Это работает, потому что у вас нет заменил объект-прототип, который вы только что добавили к нему. myRequestManager видит добавления, потому что вы добавили их к используемому им объекту (а не при установке нового объекта в свойстве prototype функции конструктора).

Почему это происходит, это немного технический вопрос, и я в основном полагаюсь на спецификацию. Когда интерпретатор входит в новый «контекст выполнения» (например, функцию или глобальный - например,, page-level - context), порядок, в котором он делает что-то, не является строгим нисходящим исходным порядком, есть фазы. Одним из первых этапов является создание экземпляров всех функций, определенных в контексте; это происходит до выполнения любого пошагового кода. Подробности во всей красе в разделах 10.4.1 (глобальный код), 10.4.3 (код функции) и 10.5 (привязки объявлений) в спецификации , но в основном функции создаются перед первой строкой пошаговый код. : -)

Это легче всего увидеть на изолированном тестовом примере:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>Test Page</title>
<style type='text/css'>
body {
    font-family: sans-serif;
}
</style>
<script type='text/javascript'>
// Uses Thing1
var User1 = (function() {
    var thing1 = new Thing1();

    function useIt() {
        alert(thing1.foo());
    }

    return useIt;
})();

// Uses Thing2
var User2 = (function() {
    var thing2 = new Thing2();

    function useIt() {
        alert(thing2.foo());
    }

    return useIt;
})();

// Thing1 gets its prototype *replaced*
function Thing1() {
    this.name = "Thing1";
}
Thing1.prototype = {
    foo: function() {
        return this.name;
    }
};

// Thing2 gets its prototype *augmented*
function Thing2() {
    this.name = "Thing2";
}
Thing2.prototype.foo = function() {
    return this.name;
};

// Set up to use them
window.onload = function() {
    document.getElementById('btnGo').onclick = go;
}

// Test!
function go() {

    alert("About to use User1");
    try
    {
        User1();
    }
    catch (e)
    {
        alert("Error with User1: " + (e.message ? e.message : String(e)));
    }

    alert("About to use User2");
    try
    {
        User2();
    }
    catch (e)
    {
        alert("Error with User2: " + (e.message ? e.message : String(e)));
    }
}

</script>
</head>
<body><div>
<div id='log'></div>
<input type='button' id='btnGo' value='Go'>
</div></body>
</html>

Как вы можете видеть, если запустить его, User1 не работает, потому что экземпляр Thing1 , который он использует, не выполняет 'не имеет свойства foo (поскольку прототип был заменен), но User2 работает, потому что экземпляр Thing2 , который он использует *, имеет * (поскольку прототип был расширен, не заменен).

6
ответ дан 4 December 2019 в 23:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: