20
function a () {
    return "foo";
}

a.b = function () {
    return "bar";
}

function c () { };
c.prototype = a;

var d = new c();
d.b(); // returns "bar"
d(); // throws exception, d is not a function

Is there some way for d to be a function, and yet still inherit properties from a?

Daniel Cassidy
  • 24,676
  • 5
  • 41
  • 54
  • 2
    This became possible now in a quite standard way, see [How to extend `Function` with ES6 classes?](http://stackoverflow.com/q/36871299/1048572) – Bergi Jun 04 '16 at 22:14

7 Answers7

11

Actually, it turns out that this is possible, albeit in a non-standard way.

Mozilla, Webkit, Blink/V8, Rhino and ActionScript provide a non-standard __proto__ property, which allow changing the prototype of an object after it has been created. On these platforms, the following code is possible:

function a () {
    return "foo";
}

a.b = function () {
    return "bar";
}

function c () {
    return "hatstand";
}
c.__proto__ = a;

c(); // returns "hatstand"
c.b(); // returns "bar"; inherited from a

This might be of use to anyone who doesn't need to worry about cross-platform compatibility.

However, note that only the properties of an object can be inherited. For example:

var d = {};
d.__proto__ = a;
d.b(); // returns "bar"
d(); // throws exception -- the fact that d is inheriting from a function
     // doesn't make d itself a function.
Daniel Cassidy
  • 24,676
  • 5
  • 41
  • 54
8

Short answer: not possible.

This line of your code:

var d = new c();

automatically assumes that d is an object. Unless c is a constructor of a builtin object, e.g., Function. But if c is already defined by the language, you cannot manipulate its prototype, and cannot "inherit" it from whatever you like. Well, in some interpreters you can, but you cannot do it safely across all interpreters — the standard says: "you doth not mess with standard objects or the interpreter will smite you!".

The builtin objects are "unique" and JavaScript does not provide ways to duplicate them. It is not possible to recreate String, Number, Function, and so on without resorting to incompatible trickery.

Eugene Lazutkin
  • 43,776
  • 8
  • 49
  • 56
  • Voting down. This is possible! AngularJS has a module called `$http`. Usage can be either: `$http({})` or `$http.post()`. This IS possible, but HOW??? – Cody Jan 29 '14 at 17:49
  • @Cody `function $http() { /* code */ }; $http.post = function() { /* code */ }` The problem is that the `post` function is defined directly on the `$http` function object, and does not come from the prototype chain, which is what this question is about. – Darth Android Jan 31 '14 at 16:22
  • @Darth Android, you are definitely right! I will correct the voting. Sorry about that. I think I have a workaround by the way -- see my edit if interested. – Cody Feb 02 '14 at 04:28
7

Based on a discussion on meta about a similar question I'm posting this answer here based on @alexander-mills original


This can now be done in a standards compliant way

First create an object which inherits Function

const obj = Object.create(Function.prototype);  // Ensures availability of call, apply ext

Then add you custom methods and properties to obj

Next declare the function

const f = function(){
    // Hello, World!
};

And set obj as the prototype of f

Object.setPrototypeOf(f,obj);

Demonstraction

const obj = Object.create(Function.prototype);

// Define an 'answer' method on 'obj'
obj.answer = function() {
  // Call this object
  this.call(); // Logs 'Hello, World'
  console.log('The ultimate answer is 42');
}

const f = function() {
  // Standard example
  console.log('Hello, World');
};

Object.setPrototypeOf(f, obj);

// 'f' is now an object with an 'answer' method
f.answer();
// But is still a callable function
f();
Zander Brown
  • 637
  • 8
  • 16
3

Yes, it is possible if you use the __proto__ property Daniel Cassidy mentioned. The trick is to have c actually return a function that has had a attached to its prototype chain.

function a () {
    return "foo";
}

a.b = function () {
    return "bar";
}

function c () {
    var func = function() {
        return "I am a function";
    };
    func.__proto__ = a;
    return func;
}
c.prototype = a;

var d = new c();
d.b(); // returns "bar"
d(); // returns "I am a function"

However, you'll need to do some more tweaking of the prototype chain if you want instanceof to return better results.

d instanceof c // true
d instanceof a // false
c instanceof a // false
nickf
  • 537,072
  • 198
  • 649
  • 721
pr1001
  • 21,727
  • 17
  • 79
  • 125
2

Does it have to actually be a prototype chain? You can use a mixin pattern to make a function have all of the properties of a instead. You can even wrap it in a nice "new" syntax to fake it if you really want.

function a () {
    return "foo";
}

a.b = function () {
    return "bar";
}

function c () {
    var f = function(){
        return a();
    };

    //mixin all properties on a
    for(var prop in a){
        f[prop] = a[prop];
    }

    return f; //just returns the function instead of "this"
};

var d = new c(); //doesn't need the new keyword, but just for fun it still works

alert(d()); //show "foo"

alert(d.b()); //shows "bar"

You can add properties to d without affecting a. The only difference between this and what you want is that changes to a will not affect existing "instances" of c.

nickf
  • 537,072
  • 198
  • 649
  • 721
Russell Leggett
  • 8,795
  • 3
  • 31
  • 45
  • Yes, I've resorted to using a mixin. In my case, there is a small chance that a will be modified, and changes ought be reflected in d, but it isn't likely to be a big deal in practise. – Daniel Cassidy Dec 04 '08 at 16:01
-1

This is something I've been trying to do for a while now. Special thanks to the authors above for their input.

Here is a chained implementation of using a "callable-object":

var $omnifarious = (function(Schema){

    var fn = function f(){
        console.log('ran f!!!');
        return f;
    };

    Schema.prototype = {
        w: function(w){ console.log('w'); return this; },
        x: function(x){ console.log('x'); return this; },
        y: function(y){ console.log('y'); return this; }
    };
    fn.__proto__ = (new Schema()).__proto__;

    return fn;
})(function schema(){ console.log('created new schema', this); });

console.log(
    $omnifarious()().w().x().y()()()
);

Also, with this "Schema" approach, it may be possible to create a reusable interface using Object.freeze() on Schema's prototype or __proto__ object. That might look something like this: fn.__proto__ = Object.freeze(new Schema().__proto__) -- this is not a practical example and more may be needed. That said, its a different discussion. Try it out and let me know!

Hope this is helpful.

Cody
  • 9,785
  • 4
  • 61
  • 46
-1

ES6 (works in "modern browsers") code sample for any number (more than 2) of functions without classes:

Function.prototype.extends = function(parent) {
    Object.setPrototypeOf(this.prototype, new parent());
  return this;
}

var a = function () {
  this.a1 = function() {
    console.log('a1');
  }  
  console.log("Am I still a function?");  
  return this;
}

var b = function () {
  this.b2 = function () {
    console.log('b2');
  };  
  console.log("Maybe?");  
  return this;
}

var c = function () {
  this.c3 = function() {
    console.log('c3');
  }    
  console.log("Yes I am!");  
  return this;
}

var f = c.extends(b.extends(a/*.extends(Function)*/));
var o = new f(); 
o.c3();
o.b2();
o.a1();
f();
VAV
  • 1,756
  • 1
  • 16
  • 26
  • Other than `Object.setPrototypeOf`, there's nothing in this code that is specific to ES6. Rather, it's a quite outdated approach. You shall [not use `new` to create prototypes](https://stackoverflow.com/questions/12592913/what-is-the-reason-to-use-the-new-keyword-here) but [rather `Object.create` (ES5)](https://stackoverflow.com/a/17393153/1048572?Benefits-of-using-Object.create-for-inheritance), and extending builtins (`Function.prototype`) is discouraged. – Bergi Mar 30 '23 at 22:44