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