Обходной путь наследования прототипов JavaScript - PullRequest
0 голосов
/ 07 октября 2011

Вот пример:

var Box = function() {
    this.id = generateUniqueId();
};
Box.prototype = {
    add:function(parent) {
        parent.appendChild(this.elm);
    }
};

var NewBox = function() {
    this.newThing = true;
};
NewBox.prototype = new Box();
NewBox.prototype.remove = function() {
    this.elm.parentNode.removeChild(this.elm);
};

var a = new NewBox();
var b = new NewBox();
alert(a.id); // alerts 0
alert(b.id); // also alerts 0! :@

Я бы хотел, чтобы a и b (в основном каждый раз, когда я создаю NewBox) имели свои собственные id.Я понимаю, что NewBox использует наследование прототипа и, таким образом, наследует от одного экземпляра сгенерированного id, который он получает от Box, но мне бы хотелось, чтобы каждый NewBox получал свой собственный id безнеобходимость явно сказать, что внутри конструктора NewBox.

Возможно, это невозможно или я делаю что-то действительно неправильное, поэтому, пожалуйста, помогите!

Спасибо большое!

Ответы [ 3 ]

6 голосов
/ 07 октября 2011

В вашем примере конструктор Box выполняется только при установке объекта NewBox.prototype.

Вы можете обойти это, вызвав функцию конструктора Box внутри NewBox с помощью Function.prototype.apply метод, чтобы установить значение this и переслать все значения аргумента, например:

//..
var NewBox = function() {
    Box.apply(this, arguments);
    this.newThing = true;
};
//..

var a = new NewBox();
var b = new NewBox();

// assuming your `generateUniqueId` function
// increments a number

alert(a.id); // will alert 1
alert(b.id); // will alert 2

Теперь каждый раз, когда вызывается конструктор NewBox для создания новогоэкземпляр объекта (new NewBox();), он вызовет функцию Box, чтобы применить к нему всю свою логику.Это поможет вам избежать повторения логики родительского конструктора снова и снова.

Используется apply, вызов функции «родительского» конструктора, устанавливающей значение this для экземпляра объекта, который создается конструктором NewBox, и мы передаем все аргументы, предоставленные этой функции.

В вашем примере также показана распространенная проблема: когда вы выражаете отношения наследования через NewBox.prototype = new Box();, вызывается конструктор Box и у него есть побочные эффекты, этот новый объект будет инициализирован, а ваша функция generateUniqueId будетвыполняется впервые, если вы хотите избежать этого, вам нужно либо использовать временный конструктор, просто чтобы создать новый объект, наследуемый от Box.prototype, либо использовать новый метод ECMAScript 5 Object.create для той же цели,например:

function inherit(o) {
  function Tmp() {}
  Tmp.prototype = o;
  return new Tmp();
}

//.....
NewBox.prototype = inherit(Box.prototype);

Или:

NewBox.prototype = Object.create(Box.prototype);

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

Наконец, но не в последнюю очередь, когда вы заменяете свойство прототипа функции всегда рекомендуетсявосстановить свойство constructor этого нового объекта-прототипа, в противном случае он будет указывать на неправильную функцию.

В вашем примере, поскольку вы заменяете NewBox.prototype новым экземпляром Box, *Свойство 1043 * будет указывать на Box, (на ваши экземпляры влияют, например, a.constructor === Box; // true) вместо NewBox, как и следовало ожидать, нам нужно установить его обратно, например:

NewBox.prototype = someObject; // as any of the examples above
NewBox.prototype.constructor = NewBox;

Вы можете абстрагировать эти детали в функцию, как я делал в функции inherit выше:

function inherits(child, parent) {
  var obj, Tmp = function () {};
  Tmp.prototype = parent.prototype;
  obj = new Tmp();
  child.prototype = obj;
  child.prototype.constructor = child;
}

//...

// instead of setting the `NewBox.prototype` manually
inherits(NewBox, Box); // "NewBox inherits from Box"

//...
2 голосов
/ 07 октября 2011

Может быть, это невозможно, или я делаю что-то действительно неправильно, поэтому, пожалуйста, помогите!

Вы делаете что-то не так.

Если вам нужны специфичные для экземпляра значения, инициализируйте их в конструкторе, а не в прототипе.

1 голос
/ 07 октября 2011

Мэтт Болл имеет правильную идею. Вместо этого попробуйте:

var Box = (function(){ 
    var numberOfBoxes = 0;
    function() {
        this.id = numberOfBoxes++;
    }
})();

Или, если вы хотите, чтобы все ваши (разные) классы имели уникальные идентификаторы:

var generateUniqueID = (function(){
    var runningCount = 0;
    return function (){
        return runningCount++;
    }
})();

var Box = function() {
    this.id = generateUniqueId();
};

var NewBox = function() {
    this.id = generateUniqueId();
    this.newThing = true;
};
...