JavaScript事件触发时如何查找嵌套函数与作用域变量?SVG拖拽示例
三个问题的共通底层原理:JavaScript 闭包机制
所有现象的核心逻辑非常直白:当外层函数makeDraggable执行完成后,它内部定义的所有函数(dragStart/dragMove/dragEnd)并不会被垃圾回收机制销毁。这些函数会永久记住自己定义时所在的词法作用域中的所有变量,哪怕外层函数已经运行结束,这些被记忆的变量依然可以被正常访问、修改,且每个实例的作用域完全独立,和其他通过同一函数生成的实例互不干扰。
针对问题1:事件触发时如何定位到makeDraggable内部的dragStart函数
- 你在执行
makeDraggable的流程中,执行了el.addEventListener('mousedown', dragStart)代码。这里传入监听器的不是函数的名称字符串,而是*dragStart函数本身的内存引用*。 - 浏览器的事件系统会把这个引用存入对应元素的事件监听器队列,根本不需要通过"全局/局部名字查找"定位函数。当对应元素触发
mousedown事件时,浏览器会直接调用队列里存储的那个函数实例——它和makeDraggable内部定义的dragStart是同一个内存对象。 - 你每对一个
.draggable元素调用一次makeDraggable,就会生成一套全新的dragStart函数实例,每个实例的引用都存在对应元素的监听器队列中,天然不会出现混淆。
针对问题2:translate/offset变量为什么能跨时间保持可访问
- 这两个变量是
makeDraggable内部用let声明的局部变量,按照常规的局部变量生命周期规则,外层函数执行完成后,这些变量本该被垃圾回收。 - 但因为
dragStart、dragMove这些内部函数在逻辑中引用了这两个变量,根据JavaScript垃圾回收规则:只要还有函数引用某个变量,该变量所在的整个词法作用域就不会被回收,会一直保留在内存中。 - 拖拽流程中,
dragStart触发时会给translate/offset赋值,后续dragMove触发时,访问的就是当初makeDraggable执行时生成的那组变量,根本不需要重新初始化。
针对问题3:入参el为什么能跨时间保留元素引用
el作为makeDraggable的形参,本质上就是外层函数作用域中的一个普通局部变量,地位和上面提到的translate/offset完全一致。- 因为
dragStart的内部逻辑用到了el,这个变量同样被闭包"记住"了——哪怕makeDraggable早就执行完成,这个变量存储的元素引用也不会丢失。 - 每次调用
makeDraggable时传入的都是不同的元素引用,生成的闭包各自存储各自的el值,自然不会出现不同可拖拽元素的引用互相干扰的问题。
内容的提问来源于stack exchange,提问作者Adam Sassano
相关产品推荐
相关产品推荐

