函数如何在javascript中工作

时间:2013-10-13 07:27:44

标签: javascript

我有一个名为Range的简单函数,它根据开始,步骤和结束值创建一个整数数组...

function Range (start, end, step) {

  // default step is 1..
  if (step === undefined ) step = 1;

  // creating an array...
  var arr = [], index = 0;

  while(start <= end) {
  arr[index]  = start ;
  index += 1;
  start += step; 
  }

  // simple function expressions
  var getAll = function () {
    return arr  ;  
  };

  var getOne = function(n) {
    return arr[n] ;
  };


  // returns a unnamed function ..
  return function(i) { 
    if (i === undefined) { return getAll() ;}
    else {return getOne(i); } 
  };  // not an iife

} 

所以基本上 Range 是一个函数,它返回一个未命名的函数,它再次返回在函数 Range 中声明的命名函数表达式..错误..我不知道..类似的东西...

现在是以下代码......

var first10 = Range (1,10) ; // no new ..() here, so no instance should be created.. only Range is called..
var first10Odd = Range(1,20,2) ; // and Range is called again..

alert(first10); // alerts - function(i) { ... }
alert(first10Odd); // alerts- function(i) { ... } 

alert(first10()) ; // alerts - 1,2,3,...10
alert(first10Odd()); // alerts - 1,3,5,...19

alert(first10(0)); // alerts - 1 
alert(first10Odd(9)); // alerts- 19

为什么警报会在注释中指定警告??我认为范围只是一个函数而不是对象构造函数,也没有创建实例...不应该一旦函数完成,函数的局部变量就会被破坏? 还是我的逻辑错误?上面的代码是怎么回事?任何人都可以解释....

i have made a fiddle of my code here..

抱歉问这个愚蠢的问题..

2 个答案:

答案 0 :(得分:4)

欢迎来到Javascript中关闭的土地。一旦你理解它们,它们就会非常强大并且非常有用。但是,如果您以前的经验是使用没有它们的语言,那么他们起初可能会感到有点异国情调。

一些答案​​/解释:

  1. 调用Range(x, y)会返回一个稍后可以调用的函数。
  2. 因为返回的函数位于另一个包含变量的函数作用域内,所以会创建一个闭包。
  3. 该闭包保持活动状态(即使外部函数已完成执行),因为存在对变量中保存的内部函数的持久引用,并且内部函数具有对外部函数中的局部变量的引用。这些引用使封闭不被垃圾收集(因此它保持活着)。
  4. 然后内部函数可以引用外部函数中的变量,包括最初传递给它的参数。
  5. 此构造允许您创建这些自定义函数,这些函数预先构建了参数。
  6. 这种闭合的概念仅存在于某些语言中。例如,它在C ++中不存在。
  7. 当通过调用Range(x,y)返回的函数本身稍后执行时,它可以使用最初在其范围内的任何变量。
  8. 每次调用Range(x,y)都会导致创建一个新的闭包。
  9. getAllgetOne是外部函数中分配了函数的局部变量。它们访问外部函数中的其他局部变量。所有这些都在前面提到的每次调用Range()时创建的闭包中。
  10. 有很多关于闭包是什么的(你可以谷歌和读取),但我喜欢把它看作一个执行上下文,它包含调用函数时范围内的所有内容(包括所有变量) )。每次调用函数时,它都会创建一个这样的执行上下文。由于javascript中的所有内容都是垃圾收集的,并且只有在没有引用时才会释放/销毁,这对于此执行上下文也是如此(例如,关闭)。只要某些东西有引用它或其中的某些东西,那么执行上下文将保持活跃状态​​,并且可以被任何可能遇到该执行上下文的代码使用。


    逐行注释:

    // first10 is assigned the anonymous function that the call to Range()
    // returned.  That anonymous function has access to the original arguments
    // passed to the Range(1,10) call and other local variables in that function.
    var first10 = Range (1,10) ; // no new ..() here, so no instance should be created.. only Range is called..
    
    // same as the call before, except this also includes the step argument
    var first10Odd = Range(1,20,2) ; // and Range is called again..
    
    // this makes sense because Range(1,10) returns a function so
    // when you alert it's value, it tells you it's a function
    alert(first10); // alerts - function(i) { ... }
    alert(first10Odd); // alerts- function(i) { ... } 
    
    // When you execute the function in first10, it runs that function 
    // and the alert shows the return value from that function
    // This particular function is set to return the entire array if nothing is passed
    // to it
    alert(first10()) ; // alerts - 1,2,3,...10
    alert(first10Odd()); // alerts - 1,3,5,...19
    
    
    // This particular function is set to return a specific index from the array
    // if an argument is passed to it
    alert(first10(0)); // alerts - 1 
    alert(first10Odd(9)); // alerts- 19
    

    如果您知道如何使用javascript调试器,则可以在内部函数中的此行if (i === undefined) { return getAll() ;}上设置断点,并且您将能够检查范围内的所有变量,包括{{1}来自外部函数的},startend


    你可能会发现这篇文章很有用,因为它封装了一些闭包可以与对象声明一起使用的方式:http://javascript.crockford.com/private.html(不完全是这里做的,但可能有助于你理解它们)。

答案 1 :(得分:1)

欢迎使用javascript闭包。让我们一行一行。

var first10 = Range(1,10);
var first10Odd = Range(1,20,2);

我们知道Range只是一个功能。所以,在这两行中,我们只是分别用2和3个参数调用Range函数。

现在,当你调用一个函数时会发生什么。显而易见的答案是,函数的主体被执行。我们在函数体中有什么。

if (step === undefined ) step = 1;

var arr = [], index = 0;

while(start <= end) {
  arr[index]  = start ;
  index += 1;
  start += step; 
}

我希望上面看到的线条很明显,你没有任何问题。

var getAll = function () {
   return arr;
};

这条线做什么?它在运行时创建一个函数。为何运行?让我们看一个例子。

<script>
   func1();
   var func1 = function() {
      alert("Hi");
   }
</script>

<script>
   func1();
   function func1() {
      alert("Hi");
   }
</script>

如果使用第一个脚本块,则会抛出错误。为什么?您正在调用尚未定义的函数。第二种情况,您在javascript解析时自己定义函数。在第一种情况下创建的函数类型称为匿名函数。让我们回到getAll。现在我们知道getAll只是一个指向匿名函数的变量,让我们来看看它的作用。它返回arr。它如何访问arr?它在函数外部声明,因此它仍然可以访问它。与

相同的情况
var getOne = function(n) {
  return arr[n] ;
};

现在非常重要的部分,

  return function(i) { 
    if (i === undefined) {
       return getAll();
    } else {
       return getOne(i);
    }
  };

它做什么?它返回一个函数。确切地说,它返回一个匿名函数。每当调用Range时,它都会创建一个新的匿名函数,该函数接受一个参数并返回它。那么,现在first10first10Odd有什么?是。你是对的,他们有功能。我希望解释

alert(first10);    // alerts - function(i) { ... }
alert(first10Odd); // alerts - function(i) { ... } 

让我们检查两个功能。在没有任何内容的情况下调用first10时,我的意思是first10(),参数i的值为undefined。所以,我们实际上是在没有参数的情况下调用匿名函数,它应该返回getAll()。如果您还记得,first10是使用Range(1,10);创建的。因此,arr现在将[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

您可能会问,当我们从函数返回时,函数内声明的变量不会超出范围。答案是肯定和否。当您只是返回一个值时。不,当你返回一个函数。返回函数时,将保持变量的状态。此属性称为closures。这就是它返回的原因

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]     for alert(first10())
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19] for alert(first10Odd())
1                                   for alert(first10(0))
19                                  for alert(first10Odd(9))

请在此处阅读有关封闭的更多信息https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures