闭包
函数嵌套,闭包 :一个函数就是一个闭包,包里面可以访问包外面任意函数,变量,而包外面对包里面是不可访问的
function a() {
var aa = "i am aa";
function b() {
var bb = "i am bb";
function c() {
var cc = "i am cc";
bb();
}
//a(); // 无限循环了
}
console.log(aa);
b();
}
a();
b(); //b is not defined
作用域链
作用域链:从全局对象衍生下来的一条链子,访问一个变量时,会从链子的末端开始查找,到顶端 window 还没找到,就报 undefined 错误了 。每个函数定义时,都会存下一个作用域链 。
作用域链对于 闭包的理解: 只能从 链子末端 向 顶端查找 。所以函数内能访问函数外,而函数外不能访问函数内。
作用域链对于 with() 的理解: with 是强制 将一个链子的 某个节点 拿过来用!
上下文
函数是一直处于上下文中的,这里的上下文指的就是 js 对象
js 浏览器中,window 对象就是最大的,从定义函数的地方开始往外推,碰见的第一个对象,就是它的上下文。
通过在 函数中 alert(this); 来判断处于哪个对象中!函数中能操作的属性和能调用的方法都是该上下文的!
函数作为返回值
不返回求和的结果,而是返回求和的函数!
function lazy_sum(arr) { var sum = function() { return arr.reduce(function(x, y) { return x + y; }); }; return sum; }
当我们调用 lazy_sum()时,返回的并不是求和结果,而是求和函数:
var f = lazy_sum([1, 2, 3, 4, 5]); // function sum() //调用函数f时,才真正计算求和的结果: f(); // 15
函数 lazy_sum 中又定义了函数 sum,内部函数 sum 可以引用外部函数 lazy_sum 的参数和局部变量,当 lazy_sum 返回函数 sum 时,相关参数和变量都保存在返回的函数中
当我们调用 lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数:
var f1 = lazy_sum([1, 2, 3, 4, 5]); var f2 = lazy_sum([1, 2, 3, 4, 5]); f1 === f2; // false f1()和f2()的调用结果互不影响。
返回的函数在其定义内部引用了局部变量 arr,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用
返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
// 每次循环,都创建了一个新的函数,然后,把创建的 3 个函数都添加到一个 Array 中返回 function count() { var arr = []; for (var i=1; i<=3; i++) { arr.push(function () { return i \* i; }); } return arr; } var results = count(); var f1 = results[0]; var f2 = results[1]; var f3 = results[2]; f1(); // 16 f2(); // 16 f3(); // 16
原因就在于返回的函数引用了变量 i,但它并非立刻执行。等到 3 个函数都返回时,它们所引用的变量 i 已经变成了 4,因此最终结果为 16。
如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:
function count() { var arr = []; for (var i = 1; i <= 3; i++) { arr.push( (function(n) { return function() { return n * n; }; })(i) ); } return arr; } var results = count(); var f1 = results[0]; var f2 = results[1]; var f3 = results[2]; f1(); // 1 f2(); // 4 f3(); // 9
Java 和 C++,要在对象内部封装一个私有变量,可以用 private 修饰一个成员变量。
在没有 class 机制,只有函数的语言里,借助闭包,同样可以封装一个私有变量。我们用 JavaScript 创建一个计数器:
'use strict'; function create_counter(initial) { var x = initial || 0; return { inc: function () { x += 1; return x; } } } var c1 = create_counter(); c1.inc(); // 1 c1.inc(); // 2 c1.inc(); // 3 var c2 = create_counter(10); c2.inc(); // 11 c2.inc(); // 12 c2.inc(); // 13
在返回的对象中,实现了一个闭包,该闭包携带了局部变量 x,并且,从外部代码根本无法访问到变量 x。换句话说,闭包就是携带状态的函数,并且它的状态可以完全对外隐藏起来。
闭包还可以把多参数的函数变成单参数的函数.
function make_pow(n) { return function(x) { return Math.pow(x, n); }; } // 创建两个新函数: var pow2 = make_pow(2); var pow3 = make_pow(3); pow2(5); // 25 pow3(7); // 343