JavaScript将作用域传递给另一个函数

39

有没有可能将一个函数的作用域传递给另一个函数?

例如,

function a(){
   var x = 5;
   var obj = {..};
   b(<my-scope>);
}

function b(){
   //access x or obj....
}

我更倾向于直接访问变量,即不使用像this.athis.obj这样的东西,而只是直接使用xobj


你所说的“范围”,是指仅限于方法,还是包括在a()中初始化的所有变量的值等?顺便说一句,这是一个好问题。 - Scherbius.com
1
此外,如果您发现问题的答案有用,请接受它们(看到那里有一个勾号),并使用点赞。这将帮助您获得更多的答案。 - Rishabh
11个回答

36
唯一真正获得访问函数a的私有作用域的方法是在a内部声明b,这样就形成了一个闭包,允许隐式访问a的变量。
以下是一些选项:
直接访问
  1. Declare b inside of a.

    function a() {
       var x = 5,
          obj = {};
       function b(){
          // access x or obj...
       }
       b();
    }
    
    a();
    
  2. If you don't want b inside of a, then you could have them both inside a larger container scope:

    function container() {
       var x, obj;
       function a(){
          x = 5;
          obj = {..};
          b();
       }
       function b(){
          // access x or obj...
       }
    }
    
    container.a();
    

除了一些额外的代码来移动变量之外,这些是你唯一可以直接在b中使用a的变量的方式。如果你能接受一些“帮助”和/或间接性,以下是一些更多的想法。

间接访问

  1. You can just pass the variables as parameters, but won't have write access except to properties of objects:

    function a() {
       var x = 5,
          obj = {};
       b(x, obj);
    }
    
    function b(x, obj){
       // access x or obj...
       // changing x here won't change x in a, but you can modify properties of obj
    }
    
    a();
    

    As a variation on this you could get write access by passing updated values back to a like so:

    // in a:
    var ret = b(x, obj);
    x = ret.x;
    obj = ret.obj;
    
    // in b:
    return {x : x, obj : obj};
    
  2. You could pass b an object with getters and setters that can access a's private variables:

    function a(){
       var x = 5,
          obj = {..},
          translator = {
             getX : function() {return x;},
             setX : function(value) {x = value;},
             getObj : function() {return obj;},
             setObj : function(value) {obj = value;}
          };
       b(translator);
    }
    
    function b(t){
       var x = t.getX(),
          obj = t.getObj();
    
       // use x or obj...
       t.setX(x);
       t.setObj(obj);
    
       // or you can just directly modify obj's properties:
       obj.key = value;
    }
    
    a();
    

    The getters and setters could be public, assigned to the this object of a, but this way they are only accessible if explicitly given out from within a.

  3. And you could put your variables in an object and pass the object around:

    function a(){
       var v = {
          x : 5,
          obj : {}
       };
       b(v);
    }
    
    function b(v){
       // access v.x or v.obj...
       // or set new local x and obj variables to these and use them.
    }
    
    a();
    

    As a variation you can construct the object at call time instead:

    function a(){
       var x = 5,
          obj = {};
       b({x : x, obj: obj});
    }
    
    function b(v){
       // access v.x or v.obj...
       // or set new local x and obj variables to these and use them.
    }
    
    a();
    

10

作用域是由函数创建的,而作用域会随着函数一起存在,所以最接近你要求的方式将是从a()传递一个函数到b(),这个函数将继续访问来自a()的作用域变量。

function a(){
   var x = 5;
   var obj = {..};
   b(function() { /* this can access var x and var obj */ });
}
function b( fn ){

    fn(); // the function passed still has access to the variables from a()

}

b()虽然无法直接访问传递给函数的变量,但是如果传递的是引用类型,比如对象,则可以通过函数返回该对象来访问。

function a(){
   var x = 5;
   var obj = {..};
   b(function() { x++; return obj; });
}
function b( fn ){

    var obj = fn();
    obj.some_prop = 'some value'; // This new property will be updated in the
                                  //    same obj referenced in a()

}

@patrick 变量 ab 中的访问在哪里?@gion 当然,好的。 :) - ErikE
@Erik:除了函数允许的操作之外,它没有任何权限。 “虽然b()无法直接访问变量...” - user113716
@Erik:嗯,这个问题的实际要求不受语言(直接访问)支持,但它确实证明了间接访问是可能的,就像a++所示。 - user113716
我指的是 b 中的 "//access a or obj..."。无论如何,我并不是要挑剔,我只是想帮助你改进你的答案。 - ErikE
@patrick_dw 我相信你可以在有漏洞的浏览器中注入 witheval 到本地作用域。 - Raynos
显示剩余2条评论

8

使用bind如何呢?

function funcA(param) {     
    var bscoped = funcB.bind(this);     
    bscoped(param1,param2...)
}

如果funcA在全局范围内定义,“this”通过绑定传递将是窗口。在funcA内部未定义的任何参数都无法在funcB内读取。或者我错过了什么?然而,当传递funcB.bind(this.funcA)时,我只能访问定义为funcA.variable的变量。 - Gabriel G

6

不。

你正在访问本地作用域对象。 [[Context]]

不能公开访问它。

现在,由于它是node.js,你应该能够编写一个C++插件,使你可以访问[[Context]]对象。我强烈建议不要这样做,因为它会给JavaScript语言带来专有扩展。


2
有任何降低投票的原因吗?如果您可以访问[[Context]],请务必告诉我。 - Raynos
刚给你点了个赞。如果有人写了这个扩展,我会很想知道,这样它就不是专有的了。让我感到惊讶的是,人们把绑定称为改变作用域。你所做的只是改变对象的引用并将其包含在函数的作用域中。要真正改变实际的“作用域”,您需要像您在这里所说的那样更改这些东西指向的框架。 - Tegra Detra

4

你不能“传递作用域”...至少我不知道怎么做。
但是你可以通过使用applycall 传递函数所引用的对象,并将当前对象 (this) 作为第一个参数,而不仅仅是调用函数:

function b(){
    alert(this.x);
}
function a(){
    this.x = 2;
    b.call(this);
}

只有在特定的作用域中声明函数,才能让该函数访问该作用域。这有点棘手。这将导致类似于以下代码:
function a(){
    var x = 1;
    function b(){
        alert(x);
    }
}

但这样做有些违背初衷。

@Erik,那是多余的。如果我想将变量作为参数传递,我就不需要作用域。 - gion_13
@gion 请看我的回答(第三个代码块)。我建议的与将它们作为参数传递有些不同。 - ErikE
@Erik 我知道你想传递一个对象来“模拟”作用域。我不是说这样做是错的或者行不通。这个方法很好,但它并不完全是这个问题的答案。 - gion_13
@gion 我发现给出更多选择比仅回答问题有帮助。例如,“你可以使用动态SQL来实现,但你可能不应该这样做,这里有其他选项”。无论如何,如果你在想我是否给你点了踩,那么并不是这样的。 - ErikE
@Erik:这与node.js无关。请参阅http://www.java-samples.com/showtutorial.php?tutorialid=829以获取更多信息。 - gion_13

2

正如其他人所说,你不能像那样传递作用域。但是,你可以使用自执行匿名函数(如果你很讲究,也可以称之为立即执行函数)来正确地定义变量的作用域:

(function(){
    var x = 5;
    var obj = {x:x};
    module.a = function(){
        module.b();
    };
    module.b = function(){
        alert(obj.x);    
    };
}());

a();

当人们不阅读问题并使用浏览器JS时,这很烦人。这与在JavaScript only问题中使用jQuery一样糟糕。thismodule而不是window,所以只需写入该模块。我还强烈建议不要覆盖global,因为那是node.js中的真正全局范围(它可能具有未知的副作用)。 - Raynos
@Raynos,我看到这个问题,就知道它是在Node中。窗口关键字在其中是因为我先在jsfiddle上进行了测试,而不是盲目地输出代码并希望它能正常工作。我的错,没有再次校对它。 - david

2

我认为最简单的方法是将变量从一个作用域传递到函数外部。如果你通过引用传递(比如对象),那么 b 就可以“访问”它(请参见以下 obj.someprop):

function a(){
   var x = 5;
   var obj = {someprop : 1};
   b(x, obj);
   alert(x); => 5
   alert(obj.someprop); //=> 'otherval'
}
function b(aa,obj){
   x += 1; //won't affect x in function a, because x is passed by value
   obj.someprop = 'otherval'; //change obj in function a, is passed by reference
}

根据给定的代码,你的 b 函数将访问全局变量 aobj,而不是在 a 中的那些。 - ErikE
是的,在b()中忘记了参数,进行了编辑,顺便重命名了变量a,因为它可能与函数名冲突(http://jshint.com/ 报错)。 - KooiInc

0

你只能使用 eval 来实现这个功能。以下代码将返回函数 b 在函数 a 的作用域中的值。

  function a(){
     var x = 5;
     var obj = {x};
     eval('('+b.toString()+'())');
  }
  
  function b(){
     //access x or obj....
     console.log(x);
  }
  
  a();  //5

远离eval.. 至少使用new Func - zergski
是的,使用 eval 不是理想的解决方案,但似乎这是唯一能够解决问题的方法。然而,提问者有可能重新设计他们的代码以消除对它的需求。 - Karol M

-2
function a(){
   this.x = 5;
   this.obj = {..};
   var self = this;
   b(self);
}
function b(scope){
   //access x or obj....

}

2
这是上下文,而不是范围。 - david

-2
function a(){
   var x = 5;
   var obj = {..};
   var b = function()
   {
        document.println(x);
   }
   b.call();
}

这不是最好的答案,但它是一个很好的。传递作用域的一种方式是在该作用域中定义函数,所以请停止对此进行投票。 - gion_13
1
Shawn,不需要使用 b.call();。直接使用 b(); 即可。 - ErikE

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