欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

JavaScript 匿名函数和闭包 - 匿名函数是没有名称的函数,闭包是可以访问函数作用域中变量的函数。

最编程 2024-07-16 08:00:36
...
//循环里包含匿名函数 
function test() {
    var arr = [];
    for (var i = 0; i < 5; i++) { 
        arr[i] = function () {
            return i;
        };
    }
    return arr;
}
var temp = test(); // 得到函数数组
alert(temp.length); // 得到函数集合长度
for (var i = 0; i < temp.length; i++) {
    alert(temp[i]()); // 输出每个函数的值,都是最后一个值
}

  上面的例子输出的结果都是 5,也就是循环后得到的最大的 i 值。因为 temp[i]调用的是匿名函数,匿名函数并没有自我执行,等到调用的时候,test()已执行完毕,i 早已变成 5,所以最终的结果就是 5 个 5

//A方式,循环里包含匿名函数,自我执行匿名函数 
function test() {
    var arr = [];
    for (var i = 0; i < 5; i++) {
        arr[i] = (function (num) { // 自我执行 
            return num;
        })(i); // 并且传参
    }
    return arr;
}
var temp = test();
for (var i = 0; i < temp.length; i++) {
    alert(temp[i]); //这里返回的是数组,直接打印即可
}


  上面的A方式 中,我们让匿名函数进行自我执行,导致最终返回给 arr[i]的是数组而不是函数了。最终导致 temp[0]-temp[4]中保留了 0,1,2,3,4 的值。

//B方式,循环里包含匿名函数,匿名函数下再做个匿名函数 
function test() {
    var arr = [];
    for (var i = 0; i < 5; i++) { 
        arr[i] = (function (num) {
            return function () { //直接返回值,这种方式变成返回函数
                return num; // 原理和A方式一 样
            }
        })(i);
    }
    return arr;
}
var temp = test();
for (var i = 0; i < temp.length; i++) {
    alert(temp[i]()); // 这里通过temp[i]()函数调用即可
}

  A方式 和B方式 中,我们通过匿名函数自我执行,立即把结果赋值给 arr[i]。每一个 i,是调用方通过按值传递的,所以最终返回的都是指定的递增的 i。而不是 test()函数里的 i。

关于 this 对象

  在闭包中使用 this 对象也可能会导致一些问题,this 对象是在运行时基于函数的执行环境绑定的,如果 this 在全局范围就是 window,如果在对象内部就指向这个对象。而闭包却在运行时指向 window 的,因为闭包并不属于这个对象的属性或方法。

var user = 'The Window'; 
var obj = {
    user : 'The Object', 
    getUserFunction : function () {
        return function () { //闭包不属于obj,里面的this指向window
            return this.user;
        };
    }
};
alert(obj.getUserFunction()()); //The window



//1.可以强制指向某个对象 
alert(obj.getUserFunction().call(obj)); //The Object

//2.也可以从上一个作用域中得到对象,那么上面的代码为:
var obj = {
    user : 'The Object', 
    getUserFunction : function () {
        var that = this; // 从 的方法里得对象
        return function () { //闭包不属于obj,里面的this指向window
            return that.user;
        };
    }
};
alert(obj.getUserFunction()()); //The Object
内存泄漏

  由于 IE 的 JavaScript 对象和 DOM 对象使用不同的垃圾收集方式,因此闭包在 IE 中会导致一些问题。就是内存泄漏的问题,也就是无法销毁驻留在内存中的元素。

function test() {
    var oDiv = document.getElementById('oDiv'); //oDiv用完之后一直驻留在内存
    oDiv.onclick = function () {
        alert(oDiv.innerHTML); // 这里用oDiv导致内存泄漏
    };
}
test();

  那么在最后应该将 oDiv 解除引用来避免内存泄漏。如果并没有使用解除引用,那么需要等到浏览器关闭才得以释放。

function test() {
    var oDiv = document.getElementById('oDiv'); 
    var text = oDiv.innerHTML;
    oDiv.onclick = function () { 
        alert(text);
    };
    oDiv = null; //解除引用
}
模仿块级作用域

JavaScript 没有块级作用域的概念。

function test(count) {
    for (var i=0; i<count; i++) {
        
    }
    alert(i); //i不会因为离开了for块就失效!!!
}
test(2);//alert的是2

  以上例子,说明JavaScript 没有块级语句的作用域,if () {} for () {}等没有作用域,如果有,出了这个范围 i 就应该被销毁了。

   JavaScript 不会提醒你是否多次声明了同一个变量;遇到这种情况,它只会对后续的声明视而不见(如果初始化了,当然还会执行的)。使用模仿块级作用域可避免这个问题。

//模仿块级作用域(私有作用域)
(function () {
//这里是块级作用域
})();

//使用块级作用域(私有作用域)改写 
function test(count) {
    (function () {
        for (var i = 0; i<count; i++) {
            
        }
    })();
    alert(i); // 报错,无法访问
}
test(2);

  使用了块级作用域(私有作用域)后,匿名函数中定义的任何变量,都会在执行结束时被销毁。这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数。一般来说,我们都应该尽可能少向全局作用域中添加变量和函数。在大型项目中,多人开发的时候,过多的全局变量和函数很容易导致命名冲突,引起灾难性的后果。如果采用块级作用域(私有作用域),每个开发者既可以使用自己的变量,又不必担心搞乱全局作用域。

(function () {
    var test = [1,2,3,4];
    alert(test); //test 出来就不认识了
})();

  在全局作用域中使用块级作用域可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁其作用域链了。

私有变量

  JavaScript 没有私有属性的概念;所有的对象属性都是公有的。不过,却有一个私有变量的概念。任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。

function test() {
    var age = 100; // 私有变量,外部无法访问
}

  而通过函数内部创建一个闭包,那么闭包通过自己的作用域链也可以访问这些变量。而利用这一点,可以创建用于访问私有变量的公有方法。

function Test() {
    var age = 100; // 私有变量
    function run() { // 私有函数
        return 'test...';
    }
    this.get = function () { // 对外公共的特权方法
    return age + run();
    };
}
var test = new Test(); 
alert(test.get());

推荐阅读