7

Scenario 1 - everything works:

var AwesomeObject = function()
{
   var self = this;
   self.whatstuff = 'really awesome';
}

AwesomeObject.prototype.doStuff = function()
{
   var self = this;
   console.log('i did '+self.whatstuff+' stuff');
   return self;
}

var awesome = new AwesomeObject(); //returns a new AwesomeObject
awesome.doStuff(); // prints 'i did really awesome stuff' on the console

Now i want it even awesomer:

var AwesomeObject = function()
{  
   var f = function() { console.log('i am awesome'); }
   var self = f;
   self.whatstuff = 'really awesome';
   return self;
}

AwesomeObject.prototype.doStuff = function()
{
   var self = this;
   console.log('i did '+self.whatstuff+' stuff');
   return self;
}

var awesome = new AwesomeObject(); //returns the interal f object
awesome(); // prints 'i am awesome'
awesome.doStuff(); // throws an error

new AwesomeObject should return an executable function itself, so that i can say 'awesome();'

but i want it to inherit the AwesomeObject.prototype, too.

adding self.prototype = AwesomeObject.prototype; does not help.

var AwesomeObject = function()
{  
   var f = function() { console.log('i am awesome'); }
   var self = f;
   self.whatstuff = 'really awesome';
   self.prototype =  AwesomeObject.prototype;
   return self;
}

ok i can copy the AwesomeObject.prototype functions - one after the other - into the scope of f

var AwesomeObject = function()
{  
   var f = function() { console.log('i am awesome'); }
   var self = f;
   self.whatstuff = 'really awesome';
   self.doStuff =  function() { AwesomeObject.prototype.doStuff.apply(self,arguments); }
   return self;
}

but i think there must be a better way, a better pattern, what is it?

this issue drives me crazy, help would be really appreciated.

in general: how to create a function object that

  • can be created with new
  • returns a function object that can be executed
  • inherits all properties and methods of a given prototype

?

is there a way?

thx Franz

MPelletier
  • 16,256
  • 15
  • 86
  • 137
Franz Enzenhofer
  • 3,666
  • 5
  • 19
  • 30
  • I reckon just copy the functions you need from the prototype. You shouldn't need to use apply(); just do self.doStuff = prototype.doStuff – Joel May 05 '11 at 10:27

3 Answers3

9

A very simple pattern is a factory.

var AwesomeObject = (function() {
    var AwesomeObject = function() {
        this.whatstuff = 'really awesome';
    };

    AwesomeObject.prototype.doStuff = function() {
        console.log('i did ' + this.whatstuff + ' stuff');
        return this;
    };

    return function() {
        var o = new AwesomeObject();
        var f = function() { console.log("I am awesome"); };
        for (var k in o) {
            f[k] = o[k];    
        }

        return f;
    };

})();

var foo = AwesomeObject();
foo();
foo.doStuff();

Live Example.

The idea is that you seperate your function and your object into two things. Your object exists in the local scope of your function and the function can use the object.

The object itself inherits completely through the prototype.

The key is do forward all properties/methods of the object onto the function.

This is the cleanest solution.

Raynos
  • 166,823
  • 56
  • 351
  • 396
  • the other answers were correct, too. this seems to be the cleanest pattern (and it works like promised). thx a lot. – Franz Enzenhofer May 05 '11 at 13:53
  • 1
    Notice that this solution does *not* return an object which inherits from your custom prototype, but that it does return a `Function` object in which some properties are mixed in. – Bergi Jan 08 '14 at 22:16
2

When a property is resolved the prototype chain is traversed as you probably know. But if you have an object awesome and try to evaluate awesome.doStuff, then awesome.prototype will never be queried for the property. You can verify this in your example, "doStuff" in awesome => false but "doStuff" in awesome.prototype => true.

So what you're doing is not changing the implicit properties of awesome, you are changing its prototype, meaning any objects created by doing new awesome will have that property. Verification: "doStuff" in new awesome() => true. And this makes sense, since there is no way to distinguish between a constructor or a regular function when using f/awesome.

The procedure when resolving a property p on an object o is roughly as follows:

  • Check whether p is defined on o
  • Check whether p is defined on o.__proto__ (usage of __proto__ is non-standard but widely implemented, except for jscript last time i checked and it has now been deprecated in SpiderMonkey)
  • Check whether p is defined on o.constructor.prototype
  • Check whether p is defined on o.constructor.prototype.prototype
  • etc

So one solution would be to simply set o.__proto__ = AwesomeClass.prototype. Think of __proto__ as a hidden intermediary object between an object and its prototype. Each instance receives its own unique __proto__ object. But this is deprecated and non-standard like I said.

We could also set the values in Function.prototype but that would override other Function properties and affect all Function instances. We don't want that.

So what's left? Not much it turns out. There is no way to set the complete prototype of an object while retaining it's inherited prototype. You will need to iterate through your prototype and copy all properties. Fortunately this will allow instanceof to behave as expected when working with chains of constructors, as well as allowing inheritance/overriding of properties properly.

The problem is really that there is no built-in way to copy the properties of an object into another one, and that there is no standard way to change an object's prototype chain ad-hoc (__proto__).

So use __proto__, or iterate through the prototype.

Adam Bergmark
  • 7,316
  • 3
  • 20
  • 23
1

I don't think there is a good way to do this. I would redesign your program to avoid it.

However, here is a bad, platform-dependent solution (works on V8 using non-standard __proto__ property):

var PrototypeToBeInherited = {'inheritedProperty': 'inheritedPropertyValue'};

f = function() {
  return "result";
};
f.__proto__ = PrototypeToBeInherited;

f()
 => "result";
f.inheritedProperty
 => "inheritedPropertyValue"

For your requirement that it must be created with "new", just wrap it in function:

F = function() {
    return f;
}
var instance = new F();
Joel
  • 11,431
  • 17
  • 62
  • 72
  • I'll second that `.__proto__` should be avoided due to platform dependence and changing the internal prototype of an object dynamically leads to nightmare code. – Raynos May 05 '11 at 11:48
  • thx a lot, it helped to ged rid of the knot i had in my thoughts – Franz Enzenhofer May 05 '11 at 13:52