JavaScript对象可以有一个原型链,但也可以是一个函数?

时间:2008-12-04 12:12:38

标签: javascript inheritance

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 个答案:

答案 0 :(得分:10)

实际上,事实证明这是可能的,尽管是以非标准的方式。

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.

答案 1 :(得分:8)

简短回答:不可能。

这段代码:

var d = new c();

自动假定d是一个对象。除非c是内置对象的构造函数,例如Function。但是如果语言已经定义了c,你就无法操纵它的原型,也不能从你喜欢的任何东西“继承”它。好吧,在一些口译员中你可以,但是你无法安全地在所有口译员中做到 - 标准说:“你不会弄乱标准物品,或者翻译会打击你!”。

内置对象是“唯一的”,JavaScript不提供复制它们的方法。如果不采用不兼容的技巧,就无法重新创建字符串,数字,函数等。

答案 2 :(得分:3)

是的,如果您使用Daniel Cassidy提到的__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

答案 3 :(得分:3)

基于discussion on meta about a similar question我在此基于@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();




答案 4 :(得分:1)

它是否必须真正成为原型链?您可以使用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"

您可以在不影响a的情况下向d添加属性。这个和你想要的唯一区别是对a的改变不会影响c的现有“实例”。

答案 5 :(得分:-1)

这是我一直试图做的事情。特别感谢上述作者的意见。

这是使用“可调用对象”的链式实现:

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的{​​{1}} Object.freeze()Schema上使用prototype创建可重复使用的__proto__宾语。这可能看起来像这样:fn.__proto__ = Object.freeze(new Schema().__proto__) - 这不是一个实际的例子,可能需要更多。那就是说,它是一个不同的讨论。尝试一下,让我知道!

希望这有用。