【问题标题】:Is it possible to create a function with another prototype than Function.prototype?是否可以使用 Function.prototype 之外的另一个原型创建函数?
【发布时间】:2011-07-05 08:29:30
【问题描述】:

我正在开发 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

是否可以为函数对象提供自定义原型,或者我是否坚持修改我创建的每个函数对象?


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

//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(…))

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

【问题讨论】:

    标签: javascript function prototype


    【解决方案1】:

    是的,您可以使用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/

    【讨论】:

      【解决方案2】:

      虽然我知道你要求一个不使用 Function.prototype 的解决方案,但我想知道你是否考虑过可以链接有条件执行的行为,这样一个 Function.prototype 函数,如 double() 将允许不同的共存行为取决于“类”实例...

      // 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 上的现有方法一起使用,这些方法不会通过抛出异常来发挥作用(只要确保包含任何预先存在的方法最后)。

      【讨论】:

      • 您还可以通过简单地遵循函数的命名约定并在 Function.prototype 方法中检查它来避免添加到原型的需要。
      【解决方案3】:

      几个不同的选项。我认为你应该考虑对象和继承而不是函数和全局。在上面的示例中,您基本上是在暗示您希望“世界”继承一组 hello 方法。 (或者你想让“hello”继承“world”?)

      您应该从这里阅读本文开始: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"
      

      【讨论】:

      • 他正在尝试修改函数对象,而您正在修改您创建的对象...
      • Jup,对于解析器组合器,我需要生成的对象是函数,因此,在您的示例中,我需要能够调用例如 earth(someParam)。所以这个继承方案对我不起作用:(
      • +1 因为这应该可以工作,但我认为您的代码对于一个小例子来说太复杂了。尤其是如果以前没有在 JavaScript 中看到任何面向对象的代码。
      • 感谢您的评论,我之前实际上已经看过面向对象的 JS 代码(我读过 Crockford,实际上很多),他只是没有回答我的问题。正如 VirtualBlackFox 所写:“他正在尝试修改函数对象,而您正在修改您创建的对象”。这是两个不同的东西。
      【解决方案4】:

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

      function Parent() {
          // constructor
      }
      
      function Child() {
          // constructor
      }
      
      Child.prototype = new Parent();
      

      以下是向 Child 类添加方法的方法:

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

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

      【讨论】:

      • 您正在举一个使用构造函数的经典继承示例。我的问题是关于向函数对象添加方法。为了在Child 上调用someMethod,您需要使用new 创建的Child 实例(因为您将someMethod 添加到prototypeChild)。该实例将是object 而不是function。这是关于向函数添加组合器,而不是向对象添加成员。我不能像函数一样使用Child 实例。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-14
      • 2020-07-17
      • 2022-01-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多