Javascript范围 - 包括不传递或制作全局

时间:2013-05-31 19:03:05

标签: javascript function parameters scope

我正在为一组函数编写一些脚本,这些函数都通过一次调用操作并获取大量参数来返回一个值。主要功能需要使用11个需要使用相同参数的其他功能。我的结构有点像这样:

function mainfunction(param1, param2, ..., param16)
{
    //do a bunch of stuff with the parameters
    return output;
}

function secondaryfunction1()
{
    //gets called by mainfunction
    //does a bunch of stuff with the parameters from mainfunction
}

我能做些什么来使传递给main函数的参数可用于所有辅助函数而不传递它们或使它们成为全局变量?如果没有,那很好,我会把它们作为参数传递 - 我很好奇我是否可以更优雅地做到这一点。

5 个答案:

答案 0 :(得分:8)

您可以将secondaryfunction1的定义放在mainfunction

function mainfunction(param1, param2, ..., param16){
    function secondaryfunction1() {
     // use param1, param2, ..., param16
    }
    secondaryfunction1();
}

<强>更新

正如@dystroy指出的那样,如果您不需要在其他地方调用secondaryfunction1,这是可行的。在这种情况下,参数列表将来自哪里 - 我不知道。

答案 1 :(得分:5)

您可以使用arguments传递secondaryFunction1 mainfunction的所有参数。但这很愚蠢。

你应该做什么,以及通常做什么,将所有参数嵌入“选项”对象中:

function mainfunction(options){
    secondaryfunction1(options);
}

function secondaryfunction1(options) {
     // use options.param1, etc.
}

// let's call it
mainfunction({param1: 0, param2: "yes?"});

这可以带来其他优势,例如

  • 命名您传递的参数,维护必须计算参数以了解要更改哪些参数不是一件好事。没有理智的库可以让你传递16个参数作为函数的直接未命名参数
  • 使您只能传递所需的参数(其他参数是默认值)

答案 2 :(得分:0)

@Igor的答案(或一些变化)是要走的路。如果你必须在其他地方使用这些函数(如@dystroy指出的那样),那么还有另一种可能性。将参数组合到一个对象中,并将该对象传递给辅助函数。

function combineEm() {
    // Get all parameters into an array.
    var args = [].slice.call(arguments, 0),
        output = {},
        i;
    // Now put them in an object
    for (i = 0; i < args.length; i++) {
        output["param" + i] = args[i];
    }
    return output;
}

从您的主要功能,您可以:

function mainfunction(param1, param2, ..., param16) {
    var params = combineEm(param1, param2, ..., param16);
    var output = secondaryfunction(params);
    // etc.
    return output;
}

答案 3 :(得分:0)

编辑:我只想澄清到目前为止提出的所有建议都有效。他们每个人都有自己的权衡利弊。

我尝试过对其他答案进行一些修改,但最终我觉得我需要发布我的解决方案。

var externalFn = function(options) {
  var str = options.str || 'hello world';

  alert(str);
};

var main = function(options) {
  var privateMethod = function() {
    var str = options.str || "foobar";

    alert("str: " + str);
  };

  // Bind a private version of an external function
        var privateMethodFromExternal = externalFn.bind(this, options);

  privateMethod();
  privateMethodFromExternal();
};

main({ str: "abc123"});
// alerts 'str: abc123'
// alerts 'abc123'
main({});
// alerts 'str: foobar'
// alerts 'hello world'
  • 似乎问题的主要观点是'main函数'使用的函数不应该继续传递选项/上下文。
  • 此示例显示如何在函数
  • 中使用privateMethods
  • 它还展示了如何使用外部函数(您可能在main之外使用)并绑定它们的私有方法版本以便在main中使用。
  • 我更喜欢使用某种“选项”对象,但这个方面对OP确实要求的范围问题并不是那么重要。您也可以使用“常规”参数。

This example can be found on codepen

答案 4 :(得分:0)

如果你对这类事情感兴趣,那么这是一个非常顽皮的解决方案。

var f1 = function() {
  var a = 1;
  var _f2 = f2.toString().replace(/^function[^{}]+{/, '');
  _f2 = _f2.substr(0, _f2.length - 2);
  eval(_f2);
}

var f2 = function(a) {
  var a = a || 0;
  console.log(a);
}

f2();   // logs 0
f1();   // logs 1

它完全在当前范围内执行某些外部函数的内容。

然而,这种诡计几乎肯定是您项目组织错误的指标。调用外部函数通常通常并不比传递一个对象困难,因为破坏性的答案建议,定义函数范围内,如 Igor&回答建议,或者将一些外部函数附加到this并主要针对this的属性编写函数。像这样:

var FunLib = {
  a : 0,
  do : function() {
    console.log(this.a);
  }
}

var Class = function() {
  this.a = 1;
  this.do = FunLib.do;
  this.somethingThatDependsOnDo = function() {
    this.a++;
    this.do();
  }
}

var o = new Class();

FunLib.do()                     // 0
o.do()                          // 1
o.somethingThatDependsOnDo();   // 2
o.do()                          // 2 now

类似地,并且可能更好 - 解决了类层次结构。

function BasicShoe {
  this.steps_taken = 0;
  this.max_steps = 100000;

  this.doStep = function() {
    this.steps_taken++;
    if (this.steps_taken > this.max_steps) {
      throw new Exception("Broken Shoe!");
    }
  }
}

function Boot {
  this.max_steps = 150000;
  this.kick_step_equivalent = 10;

  this.doKick = function() {
    for (var i = 0; i < this.kick_step_equivalent; i++) {
      this.doStep();
    }
  }
}
Boot.prototype = new BasicShoe();

function SteelTippedBoot {
  this.max_steps = 175000;
  this.kick_step_equivalent = 0;
}
SteelTippedBoot.prototype = new Boot();