封装一个事件触发所学到的前端知识

一、封装触发事件的函数

拜读了elementUI的dom.js后,感觉整个人好像打通了任督二脉,于是有感而发,想写一下关于里面的触发事件所学到的知识。

先说一下我看了哪个函数:

export const on = (function() {
  if (!isServer && document.addEventListener) {
    return function(element, event, handler) {
      if (element && event && handler) {
        element.addEventListener(event, handler, false);
      }
    };
  } else {
    return function(element, event, handler) {
      if (element && event && handler) {
        element.attachEvent('on' + event, handler);
      }
    };
  }
})();

里面涉及到的准备要讲的:

  • 函数立即执行
  • addEventListener VS attachEvent

二、先了解一下基础知识

1、(函数声明 VS 函数表达式 VS 匿名函数) => 函数的立即执行

1 函数声明的写法

                                    图1-1

如上图1-1,我们可以看到,上面这段以function关键字开头声明一个函数的,我们叫做函数声明。

函数声明有一个特点,就是js所谓的变量提升。js会在编译的时候把这种函数声明方式的函数先编译了,然后在执行阶段,即使fnName()写在fnName声明前面,也不会报错的。因为编译阶段它会把该函数写成下图1-2:

                                      图1-2

2 函数表达式

                                            图1-3

如上图1-3,是函数表达式的写法。

与函数声明不同的是,函数表达式只有在执行阶段执行到它的时候,才会执行,而在编译阶段是没有做任何事情的,也就是没有所谓的变量提升。所以如果写成下面这样:

<script>
    /* oh,NO!fnName is undefined */
    /* fnName没有被声明就调用了,js不知道fnName是什么 */
    fnName();
    let fnName = function() {
        alert('zhiyuan');
    }
</script>

那浏览器就会报错了!

当然,无论是函数声明还是函数表达式,其实我认为都应该要按顺序写下来,毕竟这是代码规范嘛~

3 匿名函数

                                                       图1-4

如上图1-4,匿名函数其实属于函数表达式的特殊方式。它常用于闭包或者函数立即执行(等下说)等,这里就不详细展开说了。

4 函数立即执行

js有一个很特别的地方,就是函数可以立即执行,而且写法很有趣。且看下图1-5:

                        图1-5

上图1-5就是函数立即执行的写法,它其实利用了匿名函数。函数立即执行有两种常用写法:

<script>
 /* (function(){...})() */
 (function(a){
   alert(a);
 })(123);
</script>

/* 或者 */

/* (function(){...}()) */
<script>
 (function(a){
   alert(a);
 }(123));
</script>

那么,函数立即执行到底是什么呢?它其实就是写了一个函数表达式,然后立即执行,如下代码所示:

<script>
/* 函数立即执行相当于 */
 var fnName = function(a) {
   alert(a);
 }(123);
</script>

/* 推理出如下 */

<script>
 var fnName = function(a) {
   alert(a);
 };
 fnName(123);
</script>

可以看出,其实a是函数的参数,而真正被传递的,是123。所以最终,浏览器会alert出123。

那么,函数立即执行还有其他写法?有的,下面给出的是函数立即执行的几种写法(这几种经常在笔试中看到):

<script>
/* (function(a){...}(123)) */
(function(a) {
    alert(a);
}(123));
/* (function(a){...})(...) */
(function(a) {
    alert(a);
})(123);
/* +运算符 */
+function(a){
    alert(a);
}(123);
/* -运算符 */
-function(a){
    alert(a);
}(123);
/* !运算符 */
!function(a){
    alert(a);
}(123);
/* 函数表达式 */
let fnName = function(a){
    alert(a);
}(123);
</script>

好,那函数作用域有什么意义呢?

实际上,函数作用域是创建了一个私人空间,在这个私人空间中,所有变量和方法都是私人的,只能在这个作用域中使用,那么这样就不会破坏污染全局的命名空间。

二、addEventListener VS attachEvent

说完函数的立即执行,再说说elementUI源码中封装on用到的addEventListener和attachEvent两个监听事件的函数。

先说说这两个函数的不同之处吧。

  • IE8(包括IE8)以下的版本都不支持addEventListener,而必须使用attachEvent兼容有趣的是:IE11不支持attachEvent了,转而支持addEventListener
  • addEventListener接受三个参数,attachEvent接受两个参数
  • addEventListener的第一个参数不需要带上on(addEventListener('click'),click前面不用加on);attachEvent就要加上on(attachEvent('onclick'))

1 addEventListener

先说一下addEventListener这个函数吧。addEventListener接受三个参数

  • event——必选参数。事件(click, mouseover等)。
  • callback——必选参数。监听到事件后的回调函数。
  • useCapture——可选。布尔值,指定事件是否在捕获或冒泡阶段执行。

用法:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>addEventListener VS attachEvent</title>
</head>
<body>
	<div id="div1">
		<button id="btn1">汁源</button>
	</div>
	<script>
        /* querySelector只能在IE8(包括IE8)以上版本才能使用 */
		var div1 = document.querySelector('#div1');
		var btn1 = document.querySelector('#btn1');
        
        /* addEventListener支持IE11和其他浏览器 */
        /* 此时点击按钮,先alert出“我是btn1”,再alert出“我是div1” */
		div1.addEventListener('click', function() {
			alert('我是div1');
		}, false);

		btn1.addEventListener('click', function() {
			alert('我是btn1');
		}, false);
	</script>
</body>
</html>

前面两个参数我们就不多说了,关键看第三个参数useCapture。useCapture接收的是布尔值,当为false的时候,表示采用冒泡方式。即事件由里到外进行冒泡。

所以,这个时候如果点击button这个元素的话,那么就会先alert出“我是btn1”,然后再alert出“我是div1”。

相反,如果useCapture为true时,表示采用捕获方式,即先触发外层html元素的事件,然后再触发里层的。

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>addEventListener VS attachEvent</title>
</head>
<body>
	<div id="div1">
		<button id="btn1">汁源</button>
	</div>
	<script>
        /* querySelector只能在IE8(包括IE8)以上版本才能使用 */
		var div1 = document.querySelector('#div1');
		var btn1 = document.querySelector('#btn1');
        
        /* addEventListener支持IE11和其他浏览器 */
        /* 此时点击按钮,先alert出“我是div1”,再alert出“我是btn1” */
		div1.addEventListener('click', function() {
			alert('我是div1');
		}, true);

		btn1.addEventListener('click', function() {
			alert('我是btn1');
		}, true);
	</script>
</body>
</html>

此时,如果点击button这个元素的话,那么就会先alert出“我是div1”,然后再alert出“我是btn1”。

2 attachEvent

attachEvent只可以在IE10(包括IE10)以下版本使用。它接收两个参数:

  • event——事件
  • callback——回调函数

用法:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>addEventListener VS attachEvent</title>
</head>
<body>
	<button id="btn1">汁源</button>
	<script>
		var btn1 = document.getElementById('btn1');
		/* 只能在IE10(包括IE10)以下的版本使用,其他浏览器不支持attachEvent */
		btn1.attachEvent('onclick', function() {
			alert(123);
		});
	</script>
</body>
</html>

注意:attachEvent方法是要带上on这个字符串的。

由此可见,写一个健壮性的触发事件的函数,需要浏览器兼容性问题。

现在,有了上面的基础,我们可以开始分析elementUI是怎么实现这个方法的。

三、elementUI的on函数

export const on = (function() {
   /* isServer是判断现在是否是浏览器环境,可以忽略掉它 */
   /**
    * 首先通过document.addEventListener判断一下浏览器是否支持addEventListener
    * 是的话就返回一个function(){addEventListener...},把这个function赋值给on这个变量
  */
  if (!isServer && document.addEventListener) {
    return function(element, event, handler) {
      if (element && event && handler) {
        element.addEventListener(event, handler, false);
      }
    };
  } else {
   /**
    * 否则返回一个function(){attachEvent...},把这个function赋值给on这个变量
    */
    return function(element, event, handler) {
      if (element && event && handler) {
        element.attachEvent('on' + event, handler);
      }
    };
  }
})();

首先,看到它是以(function(){...})(...)执行的,证明它是一个立即执行的函数,执行结果是:

export const on = function(element, event, handler) {
    if (element && event && handler) {
       element.addEventListener(event, handler, false);
    }
};

/* 或者 */

export const on = function(element, event, handler) {
    if (element && event && handler) {
       element.attachEvent('on' + event, handler);
    }
};

那么这样,我们要是用on函数(on函数就是一个函数表达式)的话,就可以如下:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>addEventListener VS attachEvent</title>
</head>
<body>
	<button id="btn1">汁源</button>
	<script>
		var on = (function() {
		  if (document.addEventListener) {
		    return function(element, event, handler) {
		      if (element && event && handler) {
		        element.addEventListener(event, handler, false);
		      }
		    };
		  } else {
		    return function(element, event, handler) {
		      if (element && event && handler) {
		        element.attachEvent('on' + event, handler);
		      }
		    };
		  }
		})();
		var btn1 = document.getElementById('btn1');
        /**
          * on
          * @param element——触发事件的元素
          * @param event——所触发的事件类型(click, mouseover等)
          * @param handler——事件触发后的回调函数 
          */
		on(btn1, 'click', function() {
		    alert('zhiyuan');
		})
	</script>
</body>
</html>

好,基本上一个事件触发的封装就到此结束了!

如果文章有哪里写的不对或者大神们有高见指导的话,希望能高抬贵手留下您宝贵的留言,本人愿意洗耳恭听!

好了,我要继续读elementUI源码提升自己,希望大家共勉!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值