是否可以使用另一个原型而不是Function.prototype创建一个函数?

时间:2011-02-27 19:20:30

标签: javascript function prototype

我正在使用JavaScript中的解析器组合器库。为此,我想创建可以像任何其他函数一样调用的函数,但也有成员函数,可以依次调用它们以根据它们所附加的函数生成输出(例如组合器)。

我当然可以将成员添加到这样的函数中:

//the functions I want to add additional members to
function hello(x) {
    return "Hello " + x;
}

function goodbye(x) {
    return "Goodbye " + x;
}

//The function I want as a member of the above functions.
//it creates another function based on the function it is 
//attached to.
function double() { 
    var that = this;
    return function(x) {
        return that(x) + ", " + that(x);
    };
}

//I can attach them manually to the function objects:
hello.double = double;
//calling hello.double()('Joe') results in => "Hello Joe, Hello Joe"
goodbye.double = double;
//calling goodbye.double()('Joe') results in => "Goodbye Joe, Goodbye Joe"

我可以创建一个功能,通过double成员扩充我的所有功能,但每次创建HeySayonara等时,我都必须记住调用它。功能。此外,我的问候语函数将直接在函数对象上为每个实例提供所有这些成员。我宁愿将它们全部放在一个原型中,并将其作为我所有问候语功能的原型。以下选项也不起作用:

  • 替换hello.__proto__(非标准,不适用于所有浏览器)
  • 直接修改Function.prototype(将这些成员添加到所有其他函数中,但它们在那里没有意义 - 我只想在我自己的一组函数上调用double

甚至可以为函数对象提供自定义原型,还是我修改了我创建的每个函数对象?


更新:我将上面的示例更改为与我正在处理的实际问题更相似。这是关于modyfing 函数对象而不是普通对象。最终目标是为解析器组合器提供舒适的语法,例如, (简化):

//parses one argument
var arg = …
//parses one function name
var name = …
//parses a function call, e.g. foo(x+y, "test", a*2)
var functionCall = name.then(elem("(")).then(arg.repSep(",")).then(")").into(process(…))

我希望能够将成员添加到一组函数中,这样,当调用这些成员时,它们会根据调用它们的函数返回新函数。这将用于解析器组合器/ monadic解析器。

4 个答案:

答案 0 :(得分:2)

是的,您可以使用setPrototypeOf

请参阅https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf

function prototype() { }
prototype.double = function() { console.log("double"); };

function myfunc() { }
Object.setPrototypeOf(myfunc, prototype);

myfunc.double();

这基本上是__proto__的标准化版本。

没有其他方法可以使用Function.prototype以外的原型直接创建函数。考虑:

function makeFunc() {
    return function x() { };
}
function protofunc { }
protofunc.double = { }...
makeFunc.prototype = protofunc;
new makeFunc()

不幸的是,通过从makeFunc构造函数返回一个函数,而不是默认的thismakeFunc.prototype不会被应用。

或者:

myFunc = Object.create(protofunc, ???);

通常我们会考虑使用Object.create来创建一个带有原型的对象,但在这种情况下,没有办法将函数指定为对象。

底线:唯一的选择是明确设置原型,您可以使用__proto__setPrototypeOf进行设置。这是ES6功能。普通浏览器支持警告适用。请参阅http://kangax.github.io/compat-table/es6/

答案 1 :(得分:1)

虽然我知道你在没有使用Function.prototype的情况下要求解决方案,但是我想知道你是否认为你可以链接有条件执行的行为,这样像Double()这样的Function.prototype函数将允许不同的共存行为取决于“类”实例......

// This could also be adapted to itself be on Function.prototype
function chainConditions () {
    var args = arguments;
    return function () {
        for (var i=0, argl = args.length; i < argl; i++) {
            var ret = args[i].apply(this, arguments);
            if (typeof (ret) !== 'undefined') { // If you want to allow undefined to be returnable, you could instead add this to a try-catch and ignore those which throw exceptions or a certain exception
                return ret;
            }
        }
    };
}
Function.prototype.double = function () {
    if (this.prototype instanceof Salutation) {
        var that = this;
        return function(x) {
            return that(x) + ", " + that(x);
        };
    }
};
Function.prototype.double = chainConditions(
    function () {
        if (this.prototype instanceof Tennis) {
            var that = this;
            return function(x) {
                return that(x) + ", " + that(x);
            };
        }
    },
    Function.prototype.double
);


function Salutation () {}

function Hello(x) {
    return "Hello " + x;
}

function Goodbye(x) {
    return "Goodbye " + x;
}
Goodbye.prototype = new Salutation();


function Tennis () {
}
function TennisPlayer (x) {   
    return x + ' serve';
}
TennisPlayer.prototype = new Tennis();



alert(TennisPlayer.double()('Forehand')); // Forehand serve, Forehand serve

alert(Goodbye.double()('Yellow Brick Road')); // Goodbye Yellow Brick Road, Goodbye Yellow Brick Road

alert(Hello.double()('Yellow Brick Road')); // does not work as we did not use Hello.prototype = new Salutation();

通过允许将一个函数用于类类型检查(例如,将Function.prototype上的所有Salutation方法一起分组为一种类型检查),可以进一步抽象它,另一个接受列表关键是他们的条件行为。

虽然它使用自己的API,但它非常突兀,甚至可以使用Function.prototype上的现有方法,这些方法不会通过抛出异常来进行(只需确保包含任何预先存在的方法最后)。

答案 2 :(得分:0)

有两种不同的选择。我认为你应该考虑对象和继承而不是函数和全局。在上面的示例中,您基本上暗示您希望“world”继承hello方法集。 (或者你想“你好”继承“世界”吗?)

您应该首先阅读本文:http://www.crockford.com/javascript/inheritance.html

这是一些使用“原型继承”

来演示继承的简单代码
// ---------------
function inherit (obj, base) {
    var tmp = function () { };
    tmp.prototype = base.prototype;
    obj.prototype = new tmp();
    obj.prototype.constructor = obj;
};

function HelloSet() {
    ;
}

HelloSet.prototype.helloA = function() {
    return "Hello A " + this.displayName;
};

HelloSet.prototype.helloB = function() {
    return "Hello B " + this.displayName;
};

function World(displayName) {
    HelloSet.constructor.call(this); // constructor chaining
    this.displayName = displayName;
}

inherit(World, HelloSet);  // World now has all the methdods of HelloSet
// ---------------

// now let's create some worlds
var earth = new World("Earth");
var mars = new World("Mars");

earth.helloA();  // returns "Hello A Earth"
mars.helloB();  // returns "Hello B Mars"

答案 3 :(得分:-2)

对你的问题的简短回答是:是的。

function Parent() {
    // constructor
}

function Child() {
    // constructor
}

Child.prototype = new Parent();

以下是如何向Child类添加方法:

Child.prototype.someMethod = function() {
    // do something
}

遗憾的是,很长一段时间我以面向对象的方式使用Javascript,但我相信有一些解决方案可以使语法更清晰。