Fractaliste
Fractaliste

Reputation: 5957

How to rename a prototype?

I get a promise object from my DB with Angular.

I want this object return true when I call myObj instanceof CustomObject, but without modify the promise comportment.

For the moment I wrote:

function CustomObject(myExistingPromise) {
   this.obj = myExistingPromise;
};

But to manipulate my promise I need to to (new CustomObject(myExistingPromise)).obj instead of new CustomObject(myExistingPromise)

Is there a way to do it?

Upvotes: 2

Views: 163

Answers (2)

T.J. Crowder
T.J. Crowder

Reputation: 1075059

TL;DR - Use duck typing instead; stick a property on the object or something and use that rather than instanceof CustomObject. You can do what you want in cutting-edge browsers, or at least get close, but it's really ugly.

The full monty:

instanceof basically walks through the prototype chain of the object and compares each entry in the chain against the prototype property of the function you refer to (e.g., obj instanceof CustomObject searches the prototype chain of obj looking for CustomObject.prototype).

So in order to retroactively make instanceof true, you'd have to insert CustomObject.prototype into the prototype chain of the object.

You cannot currently do that within the standard; there's no way to set the prototype of an existing object. In ES5, you can get the prototype via getPrototypeOf, but to set it you need setPrototypeOf, which will be in ES6 (cutting-edge versions of modern browsers already have it — IE11+, Firefox 31+, Chrome 34+; chart).

But doing it is really ugly and has significant side-effects.

Basically, what you'd do is insert CustomObject.prototype as the prototype of the promise's base prototype (the one just above Object.prototype in the chain). This means all objects created by the same constructor as the promise will also magically become instanceof CustomObject, not just the one we act on. This really does not seem like a good idea, but if you really want to do it, there's an example below.

To affect only the one instance of the object, you can't quite do it, but you can get close. You can give the object a new prototype based on CustomObject.prototype and copy over all of the properties of its original prototype that you can see (e.g., the enumerable ones). The problems are A) Perhaps that prototype has non-enumerable properties that are important, and B) That would make it no longer instanceof its original constructor (as the original constructor's prototype is no longer in the chain). (B) may or may not be a problem (most people use duck typing, not instanceof), but (A) would make the solution fairly fragile.

Bottom-line: I recommend finding a different way, rather than requiring that the object be instanceof CustomObject. Put a marker property on it or something.


Injecting CustomObject.prototype into the base of the prototype chain of the object, which will affect all objects created by the original object's constructor: (The key bit is the monkeyPatchConstructorPrototype function)

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>Inserting Prototype</title>
</head>
<body>
<script>
  (function() {
    "use strict";

    // The original constructor
    function Original() {

    }

    // The one we want to insert
    function InsertMe() {

    }

    // Create an object; also another we'll come back to
    var obj = new Original();
    var obj2 = new Original();

    // Check it
    display("Before:");
    display("obj instanceof Original? " + (obj instanceof Original));
    display("obj instanceof InsertMe? " + (obj instanceof InsertMe));

    // Insert our prototype into its chain
    monkeyPatchConstructorPrototype(obj, InsertMe);

    // Check it again
    display("After:");
    display("obj instanceof Original? " + (obj instanceof Original));
    display("obj instanceof InsertMe? " + (obj instanceof InsertMe));

    // But note that `obj2` is also affected, because we changed
    // the prototype of its prototype:
    display("Previously-created objects are also affected:")
    display("obj2 instanceof Original? " + (obj2 instanceof Original));
    display("obj2 instanceof InsertMe? " + (obj2 instanceof InsertMe));

    // And in fact all other objects created by that same prototype,
    // even in the future, are also affected:
    var obj3 = new Original();
    display("As are ones created afterward:")
    display("obj3 instanceof Original? " + (obj3 instanceof Original));
    display("obj3 instanceof InsertMe? " + (obj3 instanceof InsertMe));

    // Insert ctor.prototype into the base of the given object's
    // prototype chain. This does not only affect the given object,
    // but anything else that shares its prototype unless the object's
    // immediate prototype is Object.prototype or null.
    function monkeyPatchConstructorPrototype(o, ctor) {
      var proto;

      // No need if already done
      if (o instanceof ctor) {
        return;
      }

      // Find the root prototype of the object, the one whose
      // prototype is Object.prototype or null
      proto = Object.getPrototypeOf(o);
      while (proto !== Object.prototype && proto !== null) {
        o = proto;
        proto = Object.getPrototypeOf(o);
      }

      // Give it ctor.prototype
      Object.setPrototypeOf(o, ctor.prototype);
    }

    function display(msg) {
      var p = document.createElement('p');
      p.innerHTML = String(msg);
      document.body.appendChild(p);
    }
  })();
</script>
</body>
</html>

Live Example (Only works in cutting-edge browsers, see note and chart link above)

Output:

Before:

obj instanceof Original? true

obj instanceof InsertMe? false

After:

obj instanceof Original? true

obj instanceof InsertMe? true

Previously-created objects are also affected:

obj2 instanceof Original? true

obj2 instanceof InsertMe? true

As are ones created afterward:

obj3 instanceof Original? true

obj3 instanceof InsertMe? true

Upvotes: 2

user3299259
user3299259

Reputation:

thats rubbish because you can easily achieve the same i wrote. with

var CustomObject=angular constructor, or
var CustomObject=obj.constructor;

first generate once obj than add this if you dont know the constructor... now you can generate a new CustomObject over the Angular constructor.

function CustomObject (Angularvalue1, Angularvalue2, .....)
  {
  CustomObject.constructor.call (this,Angularvalue1, Angularvalue2, .....);
  }

CustomObject.constructor=obj.constructor; // if you know the constructor of Angluar   replace obj.constructor with the angular obj constructor function
CustomObject.prototype=obj.constructor.prototype;
CustomObject.prototype.constructor=CustomObject; //---this line would change also the constructor property of the angular constructor, remove it

var myObject=new CustomObject (Angularvalue1, Angularvalue2, .....)

Upvotes: 1

Related Questions