Может ли объект JavaScript иметь прототипную цепочку, но также быть функцией?

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

Есть ли способ для d быть функцией, но все же наследует свойства от a?

Ответ 1

Короткий ответ: невозможно.

Эта строка вашего кода:

var d = new c();

автоматически предполагает, что d является объектом. Если c не является конструктором встроенного объекта, например, Function. Но если c уже определен языком, вы не можете манипулировать его прототипом и не можете "наследовать" его из того, что вам нравится. Ну, в некоторых переводчиках вы можете, но вы не можете сделать это безопасно через всех переводчиков — в стандарте говорится: "вы не возитесь со стандартными объектами, или интерпретатор поразит вас!".

Встроенные объекты являются "уникальными", и JavaScript не предоставляет способы их дублирования. Невозможно воссоздать String, Number, Function и т.д., Не прибегая к несовместимым трюкам.

Ответ 2

На самом деле, оказывается, что этот возможен, хотя и в нестандартном способе.

Mozilla, Webkit, Blink/V8, Rhino и ActionScript предоставляют нестандартное свойство __proto__, которое позволяет изменять прототип объекта после его создания. На этих платформах возможен следующий код:

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

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

Однако обратите внимание, что наследуется только свойства объекта. Например:

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.

Ответ 3

Да, возможно, если вы используете свойство __proto__, упомянутое Даниэлем Кэссиди. Хитрость состоит в том, чтобы c действительно возвратил функцию, которая имела a, прикрепленную к своей цепочке прототипов.

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"

Однако вам нужно будет еще немного настроить цепочку прототипов, если вы хотите, чтобы instanceof возвращал лучшие результаты.

d instanceof c // true
d instanceof a // false
c instanceof a // false

Ответ 4

Нужно ли это быть прототипом? Вы можете использовать шаблон mixin, чтобы функция имела все свойства a вместо этого. Вы можете даже обернуть его в хороший "новый" синтаксис, чтобы подделать его, если вы действительно этого хотите.

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"

Вы можете добавить свойства к d, не затрагивая a. Единственное различие между этим и тем, что вы хотите, состоит в том, что изменения в не будут влиять на существующие "экземпляры" c.

Ответ 5

Основываясь на обсуждении мета о подобном вопросе Я публикую этот ответ здесь на основе @alexander-mills оригинальный


Теперь это можно выполнить стандартным образом

Сначала создайте объект, который наследует Function

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

Затем добавьте пользовательские методы и свойства в obj

Затем объявите функцию

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

И установите obj в качестве прототипа 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();

Ответ 6

Это то, что я пытался сделать какое-то время. Особая благодарность авторам за их вклад.

Вот целая реализация использования "вызываемого объекта":

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()()()
);

Кроме того, с помощью этого подхода "Схема" может быть возможно создать многоразовый interface, используя Object.freeze() в Schema prototype или __proto__. Это может выглядеть примерно так: fn.__proto__ = Object.freeze(new Schema().__proto__) - это не практический пример, и может потребоваться больше. Тем не менее, это другое обсуждение. Попробуйте и дайте мне знать!

Надеюсь, что это будет полезно.