Вопрос

Is it possible to dynamically append to the prototype of an object's instance? For example, I have two dell objects, one's a laptop and one's a desktop. I want to be able to create two instances of dell and then extend the prototype to either laptop or desktop so I can use the appropriate getters / setters.

JSFiddle link: http://jsfiddle.net/QqXgV/1/

var dell = function(){}
dell.prototype = new computer();
dell.prototype.constructor = dell;

var hp = function(){}
hp.prototype = new computer();
hp.prototype.constructor = hp;

var computer = function(){}
computer.prototype.setType = function(type){
  //Here is where I would extend.
  this.prototype extends window[type].prototype;
}

var laptop = function(){}
laptop.prototype.getKeyboard = function(){
  return "motherboard";
}

var desktop = function(){}
desktop.prototype.getKeyboard = function(){
  return "usb";
}

var dellDesktop = new dell();
dellDesktop.setType("desktop");

var dellLaptop = new dell();
dellLaptop.setType("laptop");

//This is the end goal.
dellDesktop.getKeyboard(); //Returns usb
dellLaptop.getKeyboard(); //Returns motherboard
//And then be able to do the same thing with hp.
Это было полезно?

Решение

In a case where a Dell can be either a Laptop or a Desktop one might create a DellLaptop and a DellDesktop constructor. But Laptops come in ultrabooks and netbooks so one has to create DellLaptopUltrabook and DellLaptopNetBook. So in a case where something can be either this or that and other objects can be this or that (like HP can also be either Laptop or Desktop) maybe the following pattern can help out:

var Computer = function(){};
//proxy getKeyboard based on what type it is
Computer.prototype.getKeyboard = function(){
  return this.type.getKeyboard.call(this);
};

var Dell = function(args){
  //do some checks on args here
  //re use Computer constructor
  Computer.call(this.args);
  //re use laptop or Desktop constructor code
  args.type.call(this,args);
  //re use Laptop or Desktop protype members
  this.type=args.type.prototype;

};
Dell.prototype = Object.create(Computer.prototype);
Dell.prototype.constructor = Dell;

var Laptop = function(){};
Laptop.prototype.getKeyboard = function(){
  return "motherboard";
};
//ultrabook and netbook can inherit from Laptop
//  so you could pass UltraBook or Netbook as type


var Desktop = function(){};
Desktop.prototype.getKeyboard = function(){
  return "usb";
};

var dellDesktop = new Dell({type:Desktop});

var dellLaptop = new Dell({type:Laptop});

//This is the end goal.
console.log(dellDesktop.getKeyboard()); //Returns usb
console.log(dellLaptop.getKeyboard()); //Returns motherboard

Другие советы

While the answer by HMR is the better practice, if you absolutely need to change a running instance's prototype from one object to another, use the Object.setPrototypeOf function:

let X = class {
  constructor() { this.v = 10; }
  test() { console.log(this.v); }
  morph(T) { Object.setPrototypeOf(this, T.prototype); }
};

let Y = class extends X {
  constructor() { super(); }
  test() { super.test(); console.log(10 * this.v); }
};

let Z = class { test() { console.log(34 * this.v); }};

let x = new X();

console.log(`plain test`);
x.test();

x.morph(Y);
console.log(`rewriten test 1`);
console.log(`x instanceof X?:`, x instanceof X);
console.log(`x instanceof Y?:`, x instanceof Y);
console.log(`x instanceof Z?:`, x instanceof Z);
console.log(`test result:`);
x.test();

x.morph(Z);
console.log(`rewriten test 2`);
console.log(`x instanceof X?:`, x instanceof X);
console.log(`x instanceof Y?:`, x instanceof Y);
console.log(`x instanceof Z?:`, x instanceof Z);
console.log(`test result:`);
x.test();

And note that this can even be done while in the constructor, which you should be even less tempted to do. But you can.

let X = class {
  constructor() {
    this.v = 10;
    Object.setPrototypeOf(this, Y.prototype);
  }
  test() { console.log(this.v); }
};

let Y = class extends X {
  test() { console.log(10 * this.v); }
};

let x = new X();
console.log(`x is an instance of Y:`, x instanceof Y);

And to answer the "when would you ever need this?" You might need this if you're working with, say, a polymorphic object such as a variable dimensional geometry, in which you want an intuitive constructor new Geometry(inputs) that rebinds its prototype based on the inputs, with the same API but wildly different implementations. Could you do that by telling users that they need their own code to figure out which specific constructor they need to use? Sure. But that'd be a very unpleasant experience for the user, for whom the implementation details are irrelevant, and it's that original constructor name that matters.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top