JS-функции и абстрактный класс и прототипы - PullRequest
1 голос
/ 22 марта 2019

Вот упрощенная версия моего кода:

  function TextBox () {
    this.builddom = function () {
      // Building the text dom
    }
  }
  function ImageBox () {
    this.builddom = function () {
      // Building the image dom
    }
  }
  function Box (type) {
    var handler =
      (type ==  'text') TextBox  :
      (type == 'Image') ImageBox : null;
    if (handler) (handler).call (this);

    this.builddom = function () {
      // Here I would like to call the correct builddom function for the type.
    }
  }
  var textbox = new Box ('text');
  textbox.builddom ();

Если Box.builddom не существует, это работает нормально, вызывается функция builddom, связанная с конкретным типом. Но мне нужно сделать что-то общее в Box, а затем вызвать конкретное builddom. Если я назову другое имя для Box builddom, скажем Box.dobuilddom, это тоже хорошо, но нарушает общий доступ к Boxes.

Я думаю, что некоторые умные манипуляции с прототипами могут сделать эту работу, но я не смог ее найти.

Ответы [ 6 ]

2 голосов
/ 22 марта 2019

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

function TextBox(box) {
  this.builddom = function() {    
    console.log('Building the text dom', box.props);
  }
}

function ImageBox(box) {
  this.builddom = function() {
    console.log('Building the image dom', box.props);
  }
}

function Box(props) {
  this.props = props;
  this.builddom = function() {
    throw new Error('unsupported function');
  }
}

var textbox = new TextBox(new Box({size:5}));
textbox.builddom();
1 голос
/ 22 марта 2019

Я не очень понимаю эту концепцию.Коробка - это просто контейнер.Он ничего не делает, но создает новый экземпляр.Здесь вам действительно нужен интерфейс Box, но у js нет интерфейсов.Вы можете использовать TypeScript, если хотите ...

  function TextBox () {
    this.builddom = function () {
      // Building the text dom
    }
  }
  function ImageBox () {
    this.builddom = function () {
      // Building the image dom
    }
  }

  var container = {
    createBox: function (type){
        if (type == "text")
            return new TextBox();
        else if (type == "image")
            return new ImageBox();
        else
            throw new Error();
    }
  };

  var textbox = container.createBox('text');
  textbox.builddom();

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

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

  function Box (){}

  function TextBox () {}
  TextBox.prototype = Object.create(Box.prototype, {
    constructor:TextBox,
    builddom: function () {
      // Building the text dom
    }
  });

  function ImageBox () {}
  ImageBox.prototype = Object.create(Box.prototype, {
    constructor:ImageBox,
    builddom: function () {
      // Building the image dom
    }
  });

  var container = {
    createBox: function (type){
        if (type == "text")
            return new TextBox();
        else if (type == "image")
            return new ImageBox();
        else
            throw new Error();
    }
  };

  var textbox = container.createBox('text');
  console.log(
    textbox instanceof Box, 
    textbox instanceof ImageBox, 
    textbox instanceof TextBox
  );
  textbox.builddom();
0 голосов
/ 25 марта 2019

Я предлагаю использовать композицию / делегирование, а не наследование (has-a вместо is-a).

function TextBox () {
    this.builddom = function () {
      // Building the text dom
    }
  }
  function ImageBox () {
    this.builddom = function () {
      // Building the image dom
    }
  }
  function Box (type) {
    var constructor =
      (type ==  'text') ? TextBox  :
      (type == 'Image') ? ImageBox : null;
    var delegate = new constructor();

    this.builddom = function () {
      // Pre-work goes here.
      delegate.builddom();
      // Post-work goes here.
    }
  }
  var textbox = new Box ('text');
  textbox.builddom ();
0 голосов
/ 22 марта 2019

Непонятно, почему вы не используете здесь стандартное наследование прототипов. Это позволит вам наследовать или переопределять методы родителя. Например, ImageBox наследует родительский метод, а TextBox переопределяет:

/* Define Box */
function Box (type) {
    this.type = type || 'box'
}
Box.prototype.builddom = function (){
    console.log(this.type, ": build called")
}

/* Define TextBox */
function TextBox () {
    Box.call(this, "text")
}
TextBox.prototype = Object.create(Box.prototype);

/* Override method */
TextBox.prototype.builddom = function (){
    // call parent method too?
    // Box.prototype.builddom.call(this)
    console.log(this.type, "Text box override method")
}

/* Define ImageBox */
function ImageBox () {
    Box.call(this, "image")
}
ImageBox.prototype = Object.create(Box.prototype);


var box = new Box ();
box.builddom();

var textbox = new TextBox ();
textbox.builddom();

var imageBox = new ImageBox ();
imageBox.builddom();
0 голосов
/ 22 марта 2019

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

function AbstractBox() {}
AbstractBox.prototype.builddom = function() {
  console.warn("unimplemented method");
};

function TextBox() {}
TextBox.prototype.builddom = function() {
  console.log("TextBox.builddom called");
};

function ImageBox() {}
ImageBox.prototype.builddom = function() {
  console.log("ImageBox.builddom called");
};

function ErrorBox() {}



function createBox(type) {
  var handler = Object.create(({
    "text": TextBox,
    "Image": ImageBox
  }[type] || ErrorBox).prototype);
  handler.constructor.apply(handler, [].slice.call(arguments, 1));
  for (var property in AbstractBox.prototype) {
    var method = AbstractBox.prototype[property];
    if (typeof method === "function" && !(property in handler)) handler[property] = method;
  }
  return handler;
}

(createBox("text")).builddom(); // Text
(createBox("Image")).builddom(); // Image
(createBox("error")).builddom(); // Error
0 голосов
/ 22 марта 2019

Если вы хотите использовать прототипирование, вы можете сделать что-то вроде этого:

function TextBox(props) {
    this.props = props;
}

TextBox.prototype = {
    builddom: function () {
      // Building the text dom
      console.log("TextBox", this.props);
    }
}

function ImageBox(props) {
    this.props = props;
}

ImageBox.prototype = {
    builddom: function () {
      // Building the text dom
      console.log("ImageBox", this.props);
    }
}

function Box (type, props) {
  var handler = (type ==  'text') ? TextBox :
    (type == 'Image')  ? ImageBox : null;
  if (handler) {
     handler.call(this, props);
     Object.assign(this, handler.prototype);     
  }
}

var textbox = new Box ('text', {text: 'some'});
textbox.builddom ();

var imagebox = new Box ('Image', {x: 1, y: 2});
imagebox.builddom ();
...