JavaScript函数的数组/对象引用

3

给定:

function shout(){ alert('Hello!'); }
var dasarray = [ shout ];

shout();
dasarray[0]();

shout = function(){ alert('World!'); }

shout();
dasarray[0]();

输出:

Hello!

Hello!

World!

Hello!

我本来期望dasarray接收到一个指向shout函数的引用,但是很遗憾第四个输出仍然是hello。如果我的推测是正确的,我已经成功修改了shout函数,但这表明该函数并没有被引用传递给dasarray

除了数组之外,我已经尝试过使用对象来进行示例:

dasarray = { myfunction: shout }

有着相同令人失望的结果。
我的意图是拥有一个函数的数组/对象,这些函数按顺序运行,并且可以实时扩展。
例如,通过添加新功能扩展JavaScript代码,并在正确顺序中添加额外的函数调用到现有事件中。
那么,我如何将函数通过引用放入数组/对象中?
感谢您提供的好答案。我只是想解释一下,我选择了Eli's answer,因为它引导我到了这个设置:
zzz = {
    calls: [],
    shout: function(message) {
        alert('Shouting: ' + message);
    },
    initialize: function () {
        zzz.calls.push(['shout','Hello World']);
        zzz.calls.push(['shout','this is']);
        zzz.calls.push(['shout','my last shout!']);
        zzz.run();

        zzz.shout = function(){ alert('modified!'); };
        zzz.run();
    },
    run: function () {
        $.each(zzz.calls, function(){ zzz[this[0]](this[1]); }); // <- Using jQuery $.each here, just to keep it easy
    }
};

zzz.initialize();

这是对函数的引用。你正在改变shout指向的内容,但数组仍然引用同一个函数。 - Shmiddty
2个回答

7
我本来期望 dasarray 收到了 shout 函数的引用,实际上是这样的。它没有接收到 shout 变量的引用(函数声明中使用函数名定义的符号在大多数情况下可以被视为变量,因此你随后可以对其进行赋值)。所以你之后修改变量的代码:
shout = function(){ alert('World!'); }

“dasarray”中对早期函数的引用不会受到此更改的影响。

将对象引用视为指示在何处查找对象的值。当您执行以下操作时:

function shout(){ alert('Hello!'); }

符号shout接收一个值,表明该函数在哪里找到,就像这样:

指向函数对象的变量shout

然后当你执行以下操作时:

var dasarray = [ shout ];

...这个被放置在数组的第一个条目中。现在符号shoutdasarray中的第一个条目都具有相同的值,这告诉JavaScript引擎在内存中找到该函数对象。因此我们有:

变量shout和dasarray中的条目0指向相同的函数对象

但变量shoutdasarray中的条目根本没有任何联系。

然后你做了这件事:

shout = function(){ alert('World!'); }

这段代码创建了一个新函数并将其存储在变量shout中,并记录了新函数的位置。但是,这对于存储在dasarray的第一个项0中的值没有任何影响。我们得到了这个结果:

Var shout pointing to second function, dasarray entry 0 pointing to first function

我一直在使用"value"这个词,因为这就是"对象引用"的本质:它是一个值,就像5一样。只不过这个值的作用是查找对象所在的位置。当将该值从一个变量赋值给另一个变量时,该值被复制,就像将5从一个变量复制到另一个变量时一样。

我不太喜欢Eli的解决方法,我更愿意直接简单地使用对象:

var obj = {
    shout: function() { alert("Hello!"); }
};
var dasarray = [ obj ];

obj.shout();                               // Hello!
dasarray[0].shout();                       // Hello!

obj.shout = function() { alert("World"); };

obj.shout();                               // World!
dasarray[0].shout();                       // World!

谢谢你的回答,让我明白了,但是@EliGassert也提供了一个非常好的解决方法 :) - FTav
@FTav:很高兴能帮上忙。尊重Eli,但我认为有更好的方法来做这件事。那比必要的复杂。顺便说一句,我已经提供了一个更直接的方法。 - T.J. Crowder
对于你的解决方案我没有任何疑虑。它很干净(特别是新的编辑)。给你的答案点个赞。我的解决方案非常通用,不能提供真实世界的场景。我想象再加一层或两层抽象,将dasarray与调用者分离。否则,你甚至可以更直接地将“global”改为“dasarray”并直接在数组中修改函数。但我想实际解决方案会比我们提出的例子和解决方案更复杂。 - Eli Gassert

3

我同意T.J.的回答。然而,如果你想要做一些变通,你可以创建一个函数,它总是从函数缓存中进行“及时查找”。请参见这个示例

var getFunctionRef = function(funcName)
{
    return function() { global[funcName](); };
}

var global = { };
var arr = [];

global.shout = function() { alert('hello'); }

arr[0] = getFunctionRef('shout');

global.shout = function() { alert('world'); }

arr[0]();

这只是一个非常基本的示例,有更多的方法可以实现。但这应该可以展示概念。


谢谢你,Eli。T. J.解释得非常好,为什么这不是传递引用而是传递值,但你的解决方案也把我推向了正确的方向来解决我的问题! - FTav

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