我能否在另一个函数中重新定义JavaScript函数?

4
我希望将函数引用“go”传递到另一个名为“redefineFunction”的函数中,并在“redefineFunction”内重新定义“go”。根据Johnathan Snook的说法,函数是按引用传递的,因此我不明白为什么当我将其传递到redefineFunction()中时,go()没有被重新定义。我是否遗漏了什么?
// 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

或者查看我的fiddle http://jsfiddle.net/q9Kft/


4
当对象以引用方式传递时,您将拥有对该对象属性的引用,但您不能使用新对象覆盖原始引用。 - zzzzBov
@zzzzBov:嗯,你确实有一个对对象本身的引用(不仅仅是它的属性),只是你不能覆盖该对象。 - josh3736
@josh3736,是的,我想表达的是你没有一个对象指针,而是直接拥有了这个对象。 - zzzzBov
8个回答

8
Pedants会告诉你JavaScript是纯的按值传递,但我认为这只混淆了问题,因为(当传递对象时)传递的是对该对象的引用。因此,当您修改对象的属性时,您正在修改原始对象,但如果您完全替换变量,则基本上放弃了对原始对象的引用。
如果您试图重新定义全局作用域中的函数:(这是一件坏事;通常不应该有全局函数)
function redefineFunction(fn) {
    window[fn] = function() { ... };
}

redefineFunction('go');

否则,您必须返回新函数并在调用端进行赋值。
function makeNewFunction() {
    return function() { ... };
}

go = makeNewFunction();

1
假设你真正想要做的是替换全局函数。如果你想要替换其他地方的某个函数,除非你也传递上下文,否则你会有点困难。如果你想要替换一个本地定义的函数...那你就完了。 - cHao
感谢您详细的解释。JavaScript 是一门有趣的编程语言,我总是能学到新东西。“通常不应该使用全局函数”,我之前在网页上从未考虑过这个问题。那么,全局函数的理想替代方案是什么呢?将它们封装在对象中?我已经写过一些面向对象的 JavaScript 代码,但发现它很繁琐...也许我只是太习惯 C# 了。 - Walter Stabosz
1
@Walter:JS面向对象编程需要一点时间来理解。重要的是要意识到没有类的概念。你可以让构造函数像类一样工作,语言也喜欢鼓励你尝试这样做,但当你真正尝试将它们用作类时,这种幻觉就会消失。拥抱原型模型、鸭子类型和所有其他有趣的动态特性,事情开始变得更加清晰明了。 - cHao
@WalterStabosz,我的首选方法是将您的代码包装在匿名函数中,从而限制函数/变量的范围(类似于C#类中的私有成员)。如果需要,您可以将公共函数导出到全局范围。!function() { var private; function foo() { ... }; window.bar = function() { foo(private); }; }(); -- 您可以从任何地方调用bar(),它将可以访问您的私有成员。 - josh3736
顺便说一下,这就是jQuery的写法。整个库都在一个自执行匿名函数内部(里面有许多辅助函数,你无法从外部访问),并导出 $。在你的页面中,你不能直接访问jQuery的内部变量和函数,但当你调用 $(...) 时,你可以间接地调用它们。通过这种方式,jQuery保护其函数不被覆盖,即使你声明了与jQuery函数同名的函数。 - josh3736

2
在JS中,没有“按引用传递”的概念。有时会传递引用,但它们是按值传递的。差异微妙但重要——首先,这意味着虽然可以随意操作引用对象,但无法可靠地替换对象(即更改引用本身),以任何调用者可见的方式(因为引用是按值传递的,因此仅是原始引用的副本;尝试重新分配它会导致与将arg作为数字或字符串时相同的错误)。
一些牛仔会假设您正在重新定义全局函数,并通过名称对其进行处理以避免按值传递的限制,但这会导致问题,因为您决定不在各处使用全局变量时,这些问题会立即出现。
真正的解决方案:返回新函数,并让调用者决定如何处理它。(我认为,在代码使用它们的情况下直接重新定义函数是一个非常糟糕的设计决策,但是也许有理由这样做...)

1

Snook是错的。我认为指出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”关键字,用于当你真正希望通过引用传递某些东西时。


1
我认为重要的是要提到,如果你在stuff函数内写了y.stuff = 3,那么x.stuff将会是3。在你的例子中,你正在创建一个新对象。我认为这就是一些人对整个值/引用概念感到困惑的原因。 - Bruno Silva

0

我相信函数是按值传递的。如果你把 log(f(5)); 放在你的 redefineFunction 函数里面,它会输出15,但是当你之后调用 log(go(5)) 时,它会输出10。

如果你改变 redefineFunction 的返回值,并将其赋值给 go (go = redefineFunction()),那么它将按照你的期望工作。


0

你无法在函数内部修改变量,因此快速解决方法是返回该值并在函数外部进行赋值,就像这样

// 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

0

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

function redefineFunction() {
    var fn = function(x) { return x * e; };
    return fn;
}

function go(x) {
    return x;
}

go = redefineFunction();

go(5); // return 15

0

这在火狐浏览器中可以正常工作:

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”] = “hello there”。 这是非常有用的知识。


假设您想要替换一个全局函数。如果您尝试在全局范围之外(实际上它甚至不应该存在)的任何地方使用var x = function() { }; redefineFunction('x');,那么您将会遇到一些问题。 - cHao

0
为什么不使用对象?就像这样:
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

希望能对你有所帮助!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接