【问题标题】:Can I copy/clone a function in JavaScript?我可以在 JavaScript 中复制/克隆一个函数吗?
【发布时间】:2010-10-01 15:37:44
【问题描述】:

我正在使用带有验证器插件的 jQuery。我想用我自己的一个替换“必需的”验证器。这很简单:

jQuery.validator.addMethod("required", function(value, element, param) {
    return myRequired(value, element, param);
}, jQuery.validator.messages.required);

到目前为止,一切都很好。这工作得很好。但我真正想做的是在某些情况下调用我的函数,其余情况下调用默认验证器。不幸的是,这是递归的:

jQuery.validator.addMethod("required", function(value, element, param) {
    // handle comboboxes with empty guids
    if (someTest(element)) {
        return myRequired(value, element, param);
    }
    return jQuery.validator.methods.required(value, element, param);
}, jQuery.validator.messages.required);

我查看了验证器的源代码,“required”的默认实现在 jQuery.validator.messages.required 中定义为匿名方法。所以没有其他(非匿名)对我可以使用的函数的引用。

在调用 addMethod 之前在外部存储对函数的引用并通过该引用调用默认验证器没有区别。

我真正需要做的是能够通过值而不是通过引用来复制默认所需的验证器函数。但是经过相当多的搜索,我无法弄清楚如何做到这一点。有可能吗?

如果不可能,那么我可以复制原始函数的源代码。但这会造成维护问题,除非没有“更好的方法”,否则我宁愿不这样做。

【问题讨论】:

    标签: javascript jquery validation jquery-validate


    【解决方案1】:

    存储对函数的引用 在外部调用 addMethod 之前 并通过调用默认验证器 该引用没有任何区别。

    这正是应该起作用的

    jQuery.validator.methods.oldRequired = jQuery.validator.methods.required;
    
    jQuery.validator.addMethod("required", function(value, element, param) {
        // handle comboboxes with empty guids
        if (someTest(element)) {
            return myRequired(value, element, param);
        }
        return jQuery.validator.methods.oldRequired(value, element, param);
    }, jQuery.validator.messages.required);
    

    这也应该可以:(this 的问题已经解决)

    var oldRequired = jQuery.validator.methods.required;
    jQuery.validator.addMethod("required", function(value, element, param) {
        // handle comboboxes with empty guids
        if (someTest(element)) {
            return myRequired(value, element, param);
        }
        return oldRequired.call(this, value, element, param);
        // return jQuery.oldRequired.apply(this, arguments);
    }, jQuery.validator.messages.required);
    

    【讨论】:

    • 它修复了递归(鉴于此,不知道为什么我的没有,因为我使用“var oldRequired”而不是 jvmoldrequired”但在其他方面是相同的,但“this”现在在 oldRequired 中是错误的. 我会努力的,但是谢谢,我想我现在走在正确的轨道上。
    • crescentfresh 的提示修复了“这个”。
    • 但这不是一个好的解决方案。最好的方法是使用 'var oldRequired' (不知道为什么它不起作用!)和 oldRequired.call(jQuery.validator.methods, ..., ..., ...)
    【解决方案2】:
    Function.prototype.clone = function() {
        var fct = this;
        var clone = function() {
            return fct.apply(this, arguments);
        };
        clone.prototype = fct.prototype;
        for (property in fct) {
            if (fct.hasOwnProperty(property) && property !== 'prototype') {
                clone[property] = fct[property];
            }
        }
        return clone;
    };
    

    唯一不好的是原型没有被克隆,所以你不能真正改变它...... 我正在研究一种克隆任何类型对象的方法,而我只剩下 RegExp 要做。 所以我明天可能会编辑并放置整个代码(如果你只将它用于函数和对象,那么它有点长并且没有优化。

    并且评论其他答案,使用 eval() 是完全愚蠢的,而且太长了,并且 Function 构造函数是一样的。字面量要好得多。 并且仅仅为此包括 JQuery,而且如果它不能正常工作(我认为它不能正常工作)并不是你能做的最聪明的事情。

    【讨论】:

      【解决方案3】:

      这是一个更新的答案

      var newFunc = oldFunc.bind({}); //clones the function with '{}' acting as it's new 'this' parameter
      

      然而 .bind 是 JavaScript 的一项新功能,但 Mdn 提供了一种解决方法

      https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind

      此外,它不会克隆函数的附加属性。

      注意:正如@gsnedder 指出的那样:绑定语句,您提供的“this”参数(上面的空白{})。无论是否通过 apply()/call() 函数覆盖,都将保留该函数的任何未来调用。

      这对你有利也可能不利,这取决于你如何处理它。

      【讨论】:

      • 然而,它依赖于你总是想用相同的 thisArg 调用函数。
      • @gsnedders :并非如此,可以定义克隆函数的 this 参数。它是绑定函数的第一个参数。然而,这可能是一把双刃剑。虽然它强制“this”与单个预定义对象保持一致,但无论您如何传递和执行函数。您无法将“this”参数恢复为您正在调用的对象(假设它不同)。除非您再次使用 apply() 覆盖它。最后,选择是好的,只要你知道你在做什么。以及您的功能是如何制作的,这应该不是问题。
      • 对,可以定义。正如我所说,“相同”。但是你不能用 apply 覆盖它,因为绑定的函数总是使用绑定的 this。
      • @gsnedders :点,测试它,你是正确的应用而不是覆盖(chrome)。更新了答案,使这个相当重要的点更加明显。
      【解决方案4】:

      我认为您只是缺少一些范围界定。试试这个:

      jQuery.validator.methods._required = jQuery.validator.methods.required;
      jQuery.validator.addMethod("required", function(value, element, param) {
          // handle comboboxes with empty guids
          if (someTest(element)) {
              return myRequired(value, element, param);
          }
          return jQuery.validator.methods._required.call(this, value, element, param);
      }, jQuery.validator.messages.required);
      

      【讨论】:

      • 是的,这解决了“这个”问题。谢谢!
      【解决方案5】:

      (这应该只是Can I copy/clone a function in JavaScript? 的评论......不幸的是,rep

      Function.prototype.clone=function(){
          return eval( '('+this.toString()+')' );
      }
      

      足够了,甚至

      Object.prototype.clone=function(){
          return eval( '('+this.toString()+')' );
      }
      

      关于效率的思考:

      • 人的效率远比浪费人的重要 优化或改善机器“寿命”的资源
      • 计算机和自动化系统应该可以减少人力 不要增加它
      • 计算开销必须严重影响结果的适口性 供许多人消费,以证明投资的合理性 优化代码的努力往往变得更加晦涩、神秘和 如此深奥,以至于无法再被一个稳定的人所理解 经过数小时试图“理解”逻辑的程序员

      关于克隆:这个问题可能是相当修辞的

      • “克隆”javascript 对象是什么意思?尤其是在递归函数论的背景下
        • 合理化克隆的一个共同主题是,它可以隔离并“保护”一个起源者免受其分身变化的影响
        • 如果a = new Object(); b = new Object();ab 的克隆? o = Object; a = new o(); b = new o(); 怎么样
      • 真的需要吗?
      • 克隆在哪里停止?是原型属性是 也隔离,因此克隆对象对这些的更改不会影响 与克隆对象无关的实例?在这种情况下 克隆必须沿着原型链一路向上直到 原始构造函数本身需要以某种方式 克隆和隔离(浏览器中的一个技巧是打开一个新窗口并重新填充它,并警告opener . 等仍然可能泄漏交叉效果)

      【讨论】:

        【解决方案6】:

        如果你想克隆一个函数,试试这个:

        Function.prototype.clone=function(){
            return eval('['+this.toString()+']')[0];
        }
        

        【讨论】:

          【解决方案7】:

          听起来没有“更好的方法”。我想您可以尝试为自己制作一个自定义所需的功能,例如:

          jQuery.validator.addMethod("customRequired", function(value, element, param) {
            return myRequired(value, element, param);
          }, jQuery.validator.messages.required);
          

          听起来您已经尝试过其他所有方法。如果我误解了这个问题,我们深表歉意。

          【讨论】:

            【解决方案8】:

            回答上一篇文章,当我需要共享一个构造函数,同时保持不同的原型时,我在一个新的函数中使用了一个包装器:

            `

            init_from_arg_obj = function () {
                return new Function('init', 'for (var i in init) this[i] = init[i];');
            };
            
            constructor_A = init_from_arg_obj();
            constructor_A.prototype = {a: 'A'};
            
            constructor_B = init_from_arg_obj();
            constructor_B.prototype = {b: 'B'};
            

            `

            我运行了几个测试来验证这不会降低优秀 JS 引擎的执行速度。

            【讨论】:

              猜你喜欢
              • 2016-08-31
              • 1970-01-01
              • 2010-12-22
              • 1970-01-01
              • 2020-10-28
              • 2011-04-15
              • 2020-09-01
              • 2011-12-09
              • 2014-02-10
              相关资源
              最近更新 更多