好程序员技术分析JavaScript闭包特性详解

本文深入解析JavaScript闭包的特性及应用场景,通过多个示例代码详细解释闭包如何工作,包括其内部机制、常见陷阱及其解决方案。

好程序员技术分析JavaScript闭包特性详解,今天来总结一下js闭包的那些事,以及遇到的坑和解决方法,希望对你有所帮助。

是的,没看错标题,重要的事情要说三篇,JavaScript闭包。

首先先简要总结闭包特性:

  • 函数的局部变量在函数返回之后仍然可用
  • 栈上的内存空间在函数返回之后仍在存在,不被回收

给个例子。下面这段代码会返回一个函数的引用:

function sayHello2(name) {

    var text = 'Hello ' + name; // Local variable

    var sayAlert = function() { alert(text); }

    return sayAlert;

}

say2 = sayHello2('Bob');

say2(); // alerts "Hello Bob"

对于这段代码,C程序员可能会认为sayAlert和say2一样,都是指向一个函数的指针。但实际上它俩有一个重要区别: 在JavaScript中,你可以认为一个函数的指针变量同时拥有两个指针。一个指向这个函数,另一个隐藏的指针指向一个闭包。

重点在于你的函数内是否引用的外部变量。

在JavaScript中,如果你在一个函数内定义一个新的函数,那么这个新的函数就是一个闭包。 对于C或者其他高级语言,函数执行结束并返回之后,它所占用的栈空间将被释放回收。函数内定义的局部变量将不再可用。但在JavaScript中,并不这样。如上所示,函数执行结束后,它所占用的栈空间并不会被全部回收。

上面是基本理论。更进一步,再来一个例子:

    // Local variable that ends up within closure

    var num = 666;

    var sayAlert = function() { alert(num); }

    num++;

    return sayAlert;

}

var sayNumber = say667();

sayNumber(); // alerts 667

这个例子说明:闭包中使用的函数局部变量并非是值拷贝,而是引用。say667()执行结束之后number所在的那块内存的值为667,而sayNumber()是在say667()执行结束之后才执行,当它访问number所在的内存时,结果自然也是667。

再进一步,看看用closure时易发生的错误的例子:

function buildList(list) {

    var result = [];

    for (var i = 0; i < list.length; i++) {

        var item = 'item' + list[i];

        result.push( function() {alert(item + ' ' + list[i])} );

    }

    return result;

}

 

function testList() {

    var fnlist = buildList([1,2,3]);

    // Using j only to help prevent confusion -- could use i.

    for (var j = 0; j < fnlist.length; j++) {

        fnlist[j]();

    }

}

时刻保持清醒:变量是在内存里的,闭包使用的是内存的引用而不是那块内存的值拷贝。

当你在循环中定义函数(闭包)的时候得小心,它可能并不像你最开始想的那样工作。关键有两个:

  • 子函数使用的是外部函数的局部变量的引用。
  • 循环内只是定义了子函数,并没有执行这个字函数。

最后,来一个最抽象的例子:

function newClosure(someNum, someRef) {

    // Local variables that end up within closure

    var num = someNum;

    var anArray = [1,2,3];

    var ref = someRef;

    return function(x) {

        num += x;

        anArray.push(num);

        alert('num: ' + num +

            '\nanArray ' + anArray.toString() +

            '\nref.someVar ' + ref.someVar);

      }

}

obj = {someVar: 4};

fn1 = newClosure(4, obj);

fn2 = newClosure(5, obj);

fn1(1); // num: 5; anArray: 1,2,3,5; ref.someVar: 4;

fn2(1); // num: 6; anArray: 1,2,3,6; ref.someVar: 4;

obj.someVar++;

fn1(2); // num: 7; anArray: 1,2,3,5,7; ref.someVar: 5;

fn2(2); // num: 8; anArray: 1,2,3,6,8; ref.someVar: 5;

这个例子说明,闭包的创建时机是在函数被调用的时候。每次函数调用都会生成一个新的闭包,也就是一块新的内存区域。因为函数每次调用都会新分配一块栈内存,这是一回事。

最后我自己来总结一下闭包:

  • 函数的局部变量在其他地方被引用
  • 闭包有两种基本情况:闭包的返回值是一个函数,它其中使用了该闭包的局部变量;闭包内定义了内部函数,内部函数引用了闭包的局部变量
  • 每次函数调用,都会生成一个新的闭包,分配新的内存

实例:(滑过tab)

window.onload= function(){

var tits = $('#tabTit1 li');

var cons = $('#tabCon1 .con');

var len = cons.length;

var liChange = function(){

for(var n=0;n<len;n++){

tits[n].className = tits[n].className.replace(/\s*cur/g,'');

cons[n].className = cons[n].className.replace(/\s*cur/g,'');

}

}

for(var i = 0; i<tits.length; i++){

tits[i].i = i;

tits[i].onmouseover = function(){

liChange();

cons[this.i].addClass('cur');

tits[this.i].addClass('cur');

}

}

};

 

内容概要:本文系统介绍了算术优化算法(AOA)的基本原理、核心思想及Python实现方法,并通过图像分割的实际案例展示了其应用价值。AOA是一种基于种群的元启发式算法,其核心思想来源于四则运算,利用乘除运算进行全局勘探,加减运算进行局部开发,通过数学优化器加速函数(MOA)和数学优化概率(MOP)动态控制搜索过程,在全局探索与局部开发之间实现平衡。文章详细解析了算法的初始化、勘探与开发阶段的更新策略,并提供了整的Python代码实现,结合Rastrigin函数进行测试验证。进一步地,以Flask框架搭建前后端分离系统,将AOA应用于图像分割任务,展示了其在实际工程中的可行性与高效性。最后,通过收敛速度、寻优精度等指标评估算法性能,并提出自适应参数调整、模型优化和并行计算等改进策略。; 适合人群:具备一定Python编程基础和优化算法基础知识的高校学生、科研人员及工程技术人员,尤其适合从事人工智能、图像处理、智能优化等领域的从业者;; 使用场景及目标:①理解元启发式算法的设计思想与实现机制;②掌握AOA在函数优化、图像分割等实际问题中的建模与求解方法;③学习如何将优化算法集成到Web系统中实现工程化应用;④为算法性能评估与改进提供实践参考; 阅读建议:建议读者结合代码逐行调试,深入理解算法流程中MOA与MOP的作用机制,尝试在不同测试函数上运行算法以观察性能差异,并可进一步扩展图像分割模块,引入更复杂的预处理或后处理技术以提升分割效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值