我有一个名为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
为什么警报会在注释中指定警告??我认为范围只是一个函数而不是对象构造函数,也没有创建实例...不应该一旦函数完成,函数的局部变量就会被破坏? 还是我的逻辑错误?上面的代码是怎么回事?任何人都可以解释....
抱歉问这个愚蠢的问题..答案 0 :(得分:4)
欢迎来到Javascript中关闭的土地。一旦你理解它们,它们就会非常强大并且非常有用。但是,如果您以前的经验是使用没有它们的语言,那么他们起初可能会感到有点异国情调。
一些答案/解释:
Range(x, y)
会返回一个稍后可以调用的函数。Range(x,y)
都会导致创建一个新的闭包。getAll
和getOne
是外部函数中分配了函数的局部变量。它们访问外部函数中的其他局部变量。所有这些都在前面提到的每次调用Range()
时创建的闭包中。有很多关于闭包是什么的(你可以谷歌和读取),但我喜欢把它看作一个执行上下文,它包含调用函数时范围内的所有内容(包括所有变量) )。每次调用函数时,它都会创建一个这样的执行上下文。由于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}来自外部函数的},start
和end
。
你可能会发现这篇文章很有用,因为它封装了一些闭包可以与对象声明一起使用的方式: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
时,它都会创建一个新的匿名函数,该函数接受一个参数并返回它。那么,现在first10
和first10Odd
有什么?是。你是对的,他们有功能。我希望解释
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