// 在闭包中使用this对象可能会导致一些问题;this对象是在运行时基于函数的执行环境绑定的; // 如果this在全局范围就是指向window,如果在对象内部就指向这个对象; // 而闭包却在运行时指向window的,因为闭包并不属于这个对象的属性或方法; var user = 'Window'; var obj = { user:'Object', getUserFunction:function(){ return function(){ // 闭包不属于obj,里面的this指向window; return this.user; }; } }; console.log(obj.getUserFunction()()); // =>Window; // 可以强制指向某个对象 console.log(obj.getUserFunction().call(obj)); // =>Object; // 也可以从上一个作用域中的得到对象 getUserFunction:function(){ var that = this; // 从对象的方法里得到this;此时that指向obj对象; return function(){ return that.user; } } console.log(obj.getUserFunction()()); // =>Object;
四 内存泄漏
// 由于IE的JScript对象和DOM对象使用不同的垃圾收集方式,因此闭包在IE中会导致内存泄漏问题,也就是无法销毁驻留在内存中的元素; function box(){ var oDiv = document.getElementById('oDiv'); // oDiv用完之后一直驻留在内存中; oDiv.onclick = function(){ alert(oDiv.innerHTML); // 这里用oDiv导致内存泄漏; }; oDiv = null; // 解除引用; } box(); // 由于匿名函数保存了一个对box()的活动对象的引用,因此就会导致无法减少oDiv的引用数; // 只要匿名函数存在,oDiv的引用数至少也是1;因此它所占用的内存就永远不会被回收; // PS:如果没有使用解除引用,那么要等到浏览器关闭才得以释放;
五 模仿块级作用域(定义并立即调用一个匿名函数)
// JS没有块级作用域的概念; // 这意味着在块语句(for语句/if语句)中定义的变量,实际上是在包含函数中而非语句中创建的; function box(count){ for(var i=0; i<count; i++){} // box(2); => count=2; i=2时循环停止,此时i=2; console.log(i); // =>2; i不会因为离开了for块就失效; } box(2); function box(count){ for(var i=0; i<count; i++){} var i; // 就算重新声明,也不会覆盖前面的值; console.log(i); } box(2); // 在JavaScript中,变量i是定义在box()的活动对象中的,因此从它有定义开始,就可以在函数内部随处访问它; // 以上两个例子,说明JavaScript没有块级语句的作用域,if(){}/for(){}等没有作用域; // 如果有作用域的话,出了这个范围i就应该被销毁; // JavaScript不会提醒是否多次声明了同一个变量;遇到这种情况,它只会对后续的声明视而不见(如果是初始化并赋值,还是会执行的); // 模仿块级作用域(私有作用域) (function(){ // 这里是块级作用域; })(); // 以上代码定义并立即调用了一个匿名函数;将函数声明包含在一对圆括号中,表示它实际上是一个函数表达式; // 使用块级作用域(私有作用域)改写 function box(count){ (function(){ for(var i=0; i<count; i++){} })(); console.log(i); // 报错,无法访问;变量i在私有作用域中,出了私有作用域即被销毁了. } box(2); // 使用了块级作用域后,匿名函数中定义的任何变量,都会在执行结束时被销毁;(i只能在循环中使用,使用后即被销毁); // 而私有作用域中能够访问变量count,是因为这个匿名函数是一个闭包,他能够访问包含作用域中的所有变量; // 这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数; // 一般来说,我们都应该尽可能少向全局作用域中添加变量和函数;过多的全局变量和函数很容易导致命名冲突; // 使用块级作用域,每个开发者既可以使用自己的变量,又不必担心搞乱全局作用域; (function(){ var box = [1,2,3,4]; console.log(box); // =>[1,2,3,4]; box出来就不被认识了; })(); // 销毁匿名函数中的变量; console.log(box); // =>box is not defined; // 在全局作用域中使用块级作用域可以减少闭包占用的内存问题;因为没有指向匿名函数的引用 // 只要函数执行完毕,就可以立即销毁其作用域链了;
六 私有变量
// JavaScript没用私有属性的概念;所有的属性都是公用的; // 不过有一个私有变量的概念:在任何函数中定义的变量,都可以认为是私有变量,因为不能在函数外部访问这些变量; // 私有变量包括函数的参数/局部变量和在函数内部定义的其他函数; function box(){ var age = 100; // 私有变量,外部无法访问; } // 而通过内部创建一个闭包,那么闭包通过自己的作用域链也可以访问这些变量; // 而利用这一点,可以创建用于访问私有变量的公用方法;特权方法; function Box(){ // 构造函数; var age = 100; // 私有变量; function run(){ // 私有函数; return '运行中...'; }; this.get = function(){ // 对外公共的特权方法; return age+run(); // 将闭包赋值给变量; }; } var box = new Box(); console.log(box.get()); // 可以通过构造方法传参来访问私有变量 function Person(name){ var user = name; // 这句其实可以省略; this.getUser = function(){ return user; }; this.setUser = function(name){ user = name; } } var p = new Person('Lee'); console.log(p.getUser()); // =>Lee; console.log(p.setUser('Jack')); console.log(p.getUser()); // =>Jack; // 但是,构造函数模式的缺点是针对每个实例都会创建同样一组新方法;而使用静态私有变量来实现特权方法就可以避免这个问题;
七 静态私有变量