【问题标题】:Can I redefine a JavaScript function from within another function?我可以从另一个函数中重新定义 JavaScript 函数吗?
【发布时间】:2023-03-16 16:20:01
【问题描述】:

我想将函数引用“go”传递给另一个函数“redefineFunction”,并在“redefineFunction”中重新定义“go”。根据 Johnathan Snook 的说法,函数是 passed by reference,所以我不明白为什么当我将 go() 传递给 redefineFunction() 时不会重新定义它。有什么我遗漏的吗?

// redefineFunction() will take a function reference and
// reassign it to a new function
function redefineFunction(fn) {
    fn = function(x) { return x * 3; };
}

// initial version of go()
function go(x) {
    return x;            
}

go(5); // returns 5

// redefine go()
go = function(x) {
     return x * 2;        
}

go(5); // returns 10

// redefine go() using redefineFunction()
redefineFunction(go);

go(5); // still returns 10, I want it to return 15

​ 或者看我的小提琴http://jsfiddle.net/q9Kft/

【问题讨论】:

  • 当对象通过引用传递时,你有一个对对象属性的引用,但是你不能用一个新的对象覆盖原来的引用。
  • @zzzzBov:嗯,你确实有一个对 object 本身的引用(不仅仅是它的属性),你只是不能覆盖这个对象。
  • @josh3736,是的,基本上我的意思是你没有指向对象的指针,你有对象。

标签: javascript function scope


【解决方案1】:

Pedants 会告诉你 JavaScript 是纯按值传递,但我认为 only clouds the issue 因为传递的 (传递对象时)是一个 引用到那个对象。因此,当您修改对象的属性时,您正在修改原始对象,但如果您完全替换变量,您实际上放弃了对原始对象的引用。

如果你试图在全局范围内重新定义一个函数:(这是一件坏事;你通常不应该有全局函数)

function redefineFunction(fn) {
    window[fn] = function() { ... };
}

redefineFunction('go');

否则,您必须返回新函数并在调用方分配。

function makeNewFunction() {
    return function() { ... };
}

go = makeNewFunction();

【讨论】:

  • 假设你真正想做的是替换一个全局函数。如果您想在其他地方替换某些功能,除非您也传入上下文,否则您有点搞砸了。如果你想替换一个本地定义的函数……你就完蛋了。
  • 感谢您的解释。 JavaScript 是一种有趣的工作语言,我总是在学习新东西。 “你通常不应该有全局函数”我从来没有在我的网页上真正考虑过这一点。全局函数的理想替代方案是什么?将它们封装在一个对象中?我做过一些面向对象的 JavaScript,但发现它很麻烦……也许我太习惯 C#了。
  • @Walter:JS OOP 需要一些时间来理解。要意识到的重要一点是没有课程。你可以让构造函数的行为有点像类,并且语言喜欢鼓励你尝试这样做,但是当你尝试真正使用它们时,这种错觉就会消失。拥抱原型模型和鸭式打字以及所有其他有趣的动态内容,事情开始变得更有意义。
  • @WalterStabosz,我首选的方法是将代码包装在匿名函数中,从而限制函数/变量的范围(想想 C# 类中的私有成员)。然后,如果需要,您可以将公共函数导出到全局范围。 !function() { var private; function foo() { ... }; window.bar = function() { foo(private); }; }(); -- 你可以从任何地方拨打bar(),它可以访问你的私人成员。
  • ...顺便说一句,这就是 jQuery 的编写方式。整个库位于一个自动执行的匿名函数中(其中有许多您无法从外部访问的辅助函数),它导出$。在您的页面中,您无法直接访问 jQuery 的内部变量和函数,但您可以在调用$(...) 时间接调用它们。这样,如果你声明一个与 jQuery 函数同名的函数,jQuery 就可以保护它的函数不被覆盖。
【解决方案2】:

在 JS 中没有什么是“通过引用传递”的。有时会传递引用,但它们是按值传递的。区别是微妙的,但很重要——一方面,这意味着虽然您可以随意操作引用的对象,但您不能可靠地替换该对象(阅读:更改引用本身) 以任何方式调用者将看到(因为引用是按值传递的,因此只是原始引用的副本;尝试重新分配它会以与 arg 是数字或字符串相同的方式中断)。

有些牛仔会假设您正在重新定义一个全局函数并通过名称来处理它以绕过按值传递的限制,但这会在您决定不在所有地方都有全局函数的第二个问题。

真正的解决方案:返回新函数,让调用者决定如何处理它。 (我认为从使用它们的代码中重新定义函数无论如何都是一个非常糟糕的设计决定,但是嗯。我想这可能是它的一个原因......)

【讨论】:

    【解决方案3】:

    斯努克错了。而且我认为指出 JavaScript 中的一切都是按值传递的一点也不迂腐 (@josh3736 :)。 Snook 的文章完全错了。传递原语和传递对象的工作方式完全相同。这些是等价的:

    var x = 2;
    var y = x;
    y = 3; //x is STILL 2.
    
    function stuff(y){
      y = 3; //guess what. x is STILL 2
    }
    
    stuff(x);
    

    //////////////////

    var x = {stuff: 2};
    var y = x;
    y = {stuff: 3}; //x.stuff is STILL 2
    
    function stuff(y){
      y = {stuff: 3}; //guess what. x.stuff is STILL 2
    }
    
    stuff(x);
    

    这很重要。 Java、C# 和大多数语言都以这种方式工作。这就是为什么当你真的确实想通过引用传递某些东西时,C# 有一个“ref”关键字。

    【讨论】:

    • 我认为重要的是要提一下,如果您在 stuff 函数中写了y.stuff = 3,x.stuff 将为3。在您的示例中,您正在创建一个新对象。我认为这就是一些人对整个价值/参考概念感到困惑的原因。
    【解决方案4】:

    你不能从函数内部修改变量,所以快速解决方法是返回值并在函数外部分配它,像这样

    // log() just writes a message to the text area
    function log(message) {
        $('#output').val($('#output').val() + message + "\n");
    }
    
    // redefineFunction() will take a function reference and
    // reassign it to a new function
    function redefineFunction() {
       newvalue = function(x) { return x * 3; };
       return newvalue;
    }
    
    // initial version of go()
    function go(x) {
        return x;            
    }
    
    log(go(5)); // returns 5
    
    // redefine go()
    go = function(x) {
         return x * 2;        
    }
    
    log(go(5)); // returns 10
    
    // redefine go() using redefineFunction()
    go = redefineFunction();
    
    log(go(5)); // returns 10, I want it to return 15
    

    【讨论】:

      【解决方案5】:

      我相信函数是“按值传递的”。如果您将log(f(5)); 放入您的redefineFunction 函数中,它将输出15,但之后调用log(go(5)) 时会输出10。

      如果您更改 redefineFunction 以返回该函数,然后将其分配给 go (go = redefineFunction()),它将按您的预期工作。

      【讨论】:

        【解决方案6】:

        这相当于询问您是否可以通过将任何变量作为参数传递给某个函数来重新定义它。不,您可以重新分配它,呃,重新分配它。在这种情况下,如果你让redefineFunction返回一个函数,你可以简单地将它分配给go

        function redefineFunction() {
            var fn = function(x) { return x * e; };
            return fn;
        }
        
        function go(x) {
            return x;
        }
        
        go = redefineFunction();
        
        go(5); // return 15
        

        【讨论】:

          【解决方案7】:

          这在 Firefox 中有效:

          function redefineFunction(fn) {
              window[fn] = function(x) {
                  return x * 3;
              }
          };
          
          function go(x) {
              return x;
          };
          
          alert(go(5));
          
          go=function(x) {
              return x * 2;
          }
          
          alert(go(5));
          
          redefineFunction('go');
          
          alert(go(5));
          


          秘诀在于一个名为 go 的全局函数也被称为 window.go 和 window["go"]。
          这也可以用于样式:element.style["overflow"] = "hidden",以及属性:
          element["value"] = "你好"。 这是一个非常有用的知识。

          【讨论】:

          • 假设你想替换一个全局函数。如果你在除全球范围之外的任何地方尝试var x = function() { }; redefineFunction('x');(它甚至不应该是),你会感到有点震惊。
          【解决方案8】:

          为什么不使用对象?像这样:

          var o = {
              go: function( x ) {
                  return x;
              },
          
              redefineFunction: function ( fn ) {
                  if (typeof fn === 'function') {
                      this.go = fn;
                  }
              }
          }
          
          console.log(o.go(5)); // return 5
          
          var fn = function (x) {
            return x * 2;
          };
          
          o.redefineFunction(fn);
          
          console.log(o.go(5));​ //return 10
          

          希望对你有帮助!

          【讨论】:

            猜你喜欢
            • 2017-03-04
            • 1970-01-01
            • 2010-10-17
            • 2021-07-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多