Может ли объект JavaScript иметь цепочку прототипов, но при этом быть функцией? - PullRequest
17 голосов
/ 04 декабря 2008
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?

Ответы [ 6 ]

10 голосов
/ 06 декабря 2008

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

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.
8 голосов
/ 04 декабря 2008

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

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

var d = new c();

автоматически предполагает, что d является объектом. Если c не является конструктором встроенного объекта, например, Function. Но если c уже определен языком, вы не можете манипулировать его прототипом и не можете «наследовать» его от того, что вам нравится. Ну, в некоторых переводчиках вы можете, но вы не можете сделать это безопасно для всех переводчиков & mdash; Стандарт гласит: «Не связывайтесь со стандартными объектами, иначе вас поразит переводчик!».

Встроенные объекты являются «уникальными», и JavaScript не предоставляет способов их дублирования. Невозможно воссоздать строку, число, функцию и т. Д., Не прибегая к несовместимым трюкам.

3 голосов
/ 06 сентября 2017

На основе обсуждения мета о похожем вопросе Я публикую здесь ответ на основе @ alexander-mills original


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

Сначала создайте объект, который наследует 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();
3 голосов
/ 11 февраля 2010

Да, это возможно, если вы используете __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
1 голос
/ 04 декабря 2008

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

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. Единственная разница между этим и тем, что вы хотите, состоит в том, что изменения в a не влияют на существующие "экземпляры" c.

0 голосов
/ 30 января 2014

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

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

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__) - это не практический пример, и может потребоваться больше. Тем не менее, это другое обсуждение. Попробуйте и дайте мне знать!

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...