Javascript:使用其他功能重新分配功能

时间:2010-10-26 00:25:43

标签: javascript function closures pass-by-reference

假设我有这两个功能:

function fnChanger(fn) {
    fn = function() { sys.print('Changed!'); }
}
function foo() {
    sys.print('Unchanged');
}

现在,如果我致电foo(),我会按预期看到Unchanged。但是,如果我先拨打fnChanger,我仍会看到Unchanged

fnChanger(foo);
foo(); //Unchanged

现在,我认为这是因为foo没有通过引用传递给fnChanger,但我可能错了。

为什么fnChanger不会更改foo来打印Changed!? 此外,如果没有太多凌乱的语法,我怎样才能fnChanger更改foo

PS:我正在使用node.js来测试所有这些东西,所以这就是sys.print的来源。

4 个答案:

答案 0 :(得分:5)

fn参数的赋值只是使该标识符指向匿名函数,外部作用域中的foo不受影响。

当您将对象作为参数传递时,可以说“引用按值传递”。该分配仅替换fn标识符所指的位置。

这就是evaluation strategy在JavaScript中的工作方式。

fnChanger函数中赋值之前,两个标识符(全局foofn参数)指向同一个函数对象:

                ---------------------------------------------
    foo ----->  |function foo { sys.print('Un changed!'); } |
                ---------------------------------------------
                   ^
                   |
    fn -------------

分配后,fn将只指向新功能:

                ---------------------------------------------
    foo ----->  | function foo { sys.print('Unchanged!'); } |
                ---------------------------------------------

                ---------------------------------------
    fn ------>  | function { sys.print('Changed!'); } |
                ---------------------------------------

你怎么能改变它?

好吧,假设foo是全局范围内的一个函数,你可以这样做:

function fnChanger(obj, name) {
    obj[name] = function() { sys.print('Changed!'); };
}

function foo() {
    sys.print('Unchanged');
}

fnChanger(this, 'foo');
foo(); // Changed!

上述方法可行,因为在fnChanger函数中,我们需要基础对象属性名称,在全局执行上下文中声明的函数被绑定作为全局对象的属性,因此我们可以通过这种方式重新赋值。

fnChanger(this, 'foo');也应该在全局作用域中执行,它将传递this值(引用此作用域中的Global对象)和属性名称,允许您创建分配给GlobalObject.foo标识符。

如果代码在函数内部,我们就无法获得基础对象,因为在这个“函数代码执行上下文”中,函数声明(变量声明和函数形式参数也)绑定为不可访问对象的属性,称为变量对象(这些变量对象的链,形成范围链),如果是这种情况,唯一的解决方法是使用eval

更多信息:

答案 1 :(得分:3)

由于@CMS指出由于范围而无法在函数内分配它。但是你可以像这样重新分配它:

var fnChanger = function() {
  return function() {
      alert('changed!');
  }
}

var foo = function() {
  alert('Unchanged');
}

foo = fnChanger();
foo();

example

答案 2 :(得分:0)

有一个优雅的方法来解决它

function fnChanger(fn_holder) {
    fn_holder["fn"] = function() { console.log('Changed!'); }
}
function foo() {
    console.log('Unchanged');
}

const foo_holder = {fn: foo}
fnChanger(foo_holder);

foo_holder["fn"]();  // Changed!

简短说明:

  1. 像散列(或数组)一样将更高级别的引用 foo_holder 传递给 fnChange
  2. 将散列 foo_holder["fn"] 中的元素重新分配给新值。这将更改内部引用指向的位置,而不是创建新引用。
  3. 那么您就可以享受更新后的功能了。

答案 3 :(得分:0)

在 Javascript 中,函数是可以被视为另一个变量的第一类对象。但是要使函数返回其结果,必须先调用它。

当您调用 fnChanger(foo) 时,fn 变量实际上会被 foo() 覆盖。

但是您没有得到结果,因为该函数从未被调用过。尝试返回它并调用如下所示,您将获得所需的答案。

function fnChanger(fn) {
  fn = function() {
    console.log('Changed!');
  }
  return fn;
}

function foo() {
  console.log('Unchanged');
}

fnChanger(foo)();
foo();