我也谈谈闭包

本文介绍了解决使用JavaScript为多个视频元素添加鼠标悬停自动播放功能时遇到的闭包问题。作者通过逐步修改代码解决了因作用域链导致的错误,并详细解释了如何利用闭包正确实现该功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我也谈谈闭包

今天第一次在实际运用中遇到闭包的问题。

我要给几个视频video增加鼠标悬停自动播放的功能。

刚开始时,我只有一个video。代码如下:

HTML

<li><video class="work-vidz" src='vids/vader-loader.mp4' loop="true" preload="auto" preload="metadata"></video>jumping-box</li>

JS

var videoNodes = document.getElementsByClassName('work-vidz');

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

  videoNodes[i].addEventListener('mouseover',function(){
    videoNodes[i].play();
  });
  videoNodes[i].addEventListener('mouseout',function(){
  videoNodes[i].pause();
  });
}

刚开始没思考代码,直接运行,报错:

Uncaught TypeError: Cannot read property ‘play’ of undefined
at HTMLVideoElement. ((index):106)
Uncaught TypeError: Cannot read property ‘pause’ of undefined
at HTMLVideoElement. ((index):109)

我想了一下,怎么能是undefined呢?看到anonymous想起了闭包,应该是i值在for循环完毕后执行了i++,由0变成了1(我这里刚开始只有一个video),鼠标移到视频上时,执行的videoNodes[i].play();就变成了videoNodes[1].play();,所以是undefined。

为了确定是i值的问题,我在F12中设置i=0,鼠标就可以控制视频的播放与暂停了。

找到了病因,我自作聪明,把代码改了改。

for(var i=0;i<videoNodes.length;i++){
  videoNodes[i].addEventListener('mouseover',play(i));
  videoNodes[i].addEventListener('mouseout',pause(i));
}

function play(i){
videoNodes[i].play();
}

function pause(i){
videoNodes[i].pause();
}

我觉得,新建立两个函数,分别控制播放暂停,并且能接收参数,这样就可以把for循环不同的i值传递给每个video了。但是:

Uncaught (in promise) DOMException: The play() request was interrupted by a call to pause().

错误大概是播放多个视频引起的。

我犯的错误是:因为我想传递参数i,在EventListener中的这种写法play(i),直接就会执行。

如果不传参,只把playpause函数的引用写在EventListener中,这样就无法区分不同的视频了。

于是再修改一下代码,增加个函数的返回:匿名函数。

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

  videoNodes[i].addEventListener('mouseover',play(i));
  videoNodes[i].addEventListener('mouseout',pause(i));
}

  function play(i){
    return function(){
      videoNodes[i].play();
    }
  }

  function pause(i){
    return function(){
      videoNodes[i].pause();
    }  
  } 

测试正常。这个return function就是一个闭包,匿名函数中的i就会顺着作用域链一级级向函数外部查找。

play()pause()也修改成匿名函数直接放入EventListener

 var videoNodes = document.getElementsByClassName('work-vidz');

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

  videoNodes[i].addEventListener('mouseover',(function(id){
    return function(){
      videoNodes[id].play();
    }
  })(i));
  videoNodes[i].addEventListener('mouseout',(function(id){
    return function(){
      videoNodes[id].pause();
    }
  })(i));
}

外层的匿名函数其实和之前定义的播放与暂停函数类似,都是传递一个参数,然后执行,内部是返回另一个匿名函数。

这里需要注意的是id不能写成i,不然最终执行时还会顺着作用域链寻找i值,从而找到最外层的for循环中的i值。

通过这个例子,我观察到闭包的核心是变量在作用域链中寻找值的过程。以前之所以迷糊,一是没有尝试过把匿名函数改成非匿名函数,二是对于匿名函数传参的方法不熟悉:

(function(var){  
  //todo sth.
  alert(var);
})();

THE

END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值