整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

javascript看qq中多事件绑定的执行顺序

javascript看qq中多事件绑定的执行顺序

件绑定的执行次序

在W3C的标准下,事件绑定使用的是addEventLisenter。

但微软这家伙经常不按常理出牌。它提供的事件绑定方法是。

关于attachEvent的种种不足,先不一一列举。或许我们已经忍受着用attachEvent来绑定事件。

//object.attachEvent(event, function);

el.attachEvent('onclick', method);

但当在同一元素上绑定多次,你就会知道,微软的这个私人的方法又是多么“不可理喻”。

el.attachEvent("onclick", method1);

el.attachEvent("onclick", method2);

el.attachEvent("onclick", method3);

el.attachEvent("onclick", method4);

在各个IE下运行,点击el后,各个方法的执行次序是:

???? IE6/IE7: method2 -> method4 -> method3 -> method1

???? IE8: method4 -> method3 -> method2 -> method1

???? IE9: method1 -> method2 -> method3 -> method4

IE6/7是一种看不出规律的随机执行,IE8是逆序执行,IE9是顺序执行。

无论常识还是W3C标准,这种同一个事件绑定了多个方法的情况,方法的执行次序都应该是先绑定先执行。从IE6-IE9的演变,可以看得出微软还是有在不断改进的。但毕竟各个版本的IE存在,使得我们不得不注意绑定在同一事件上的多个方法之间不要有顺序依赖。

????? YUI2的事件绑定并没有对执行次序做兼容,如果有些场景的确需要绑定多个方法且方法间需要顺序执行。那我们就只能自己实现了,大致思路是:在先判断绑定的el对象上是否已经同一个事件类型的handlers,如果有则不会重复绑定而是把该对象的handler合并到handlers中成为一个方法,相当于这样function c() { a(); b(); },按照顺序的压入方法实现在IE下不会出现绑定多个方法时能顺序执行。

function addEvent(el, type, handler) {

???????? if (el.attachEvent) { // IE

???????????????? // 为元素的事件类型创建一个哈希表

???????????????? el._events=el._events || {};

???????????????? if (!handler) { return; }

???????????????? // 取得事件处理函数的数组的引用

???????????????? var handlers=el._events[type];

???????????????? if (handlers) {

????????????????????????? if(typeof handler._index==='undefined' || handlers[handler._index] !==handler) {

?????????????????????????????????? // 将事件处理函数存入数组

?????????????????????????????????? handlers.push(handler);

?????????????????????????????????? // 为事件处理函数增加索引

?????????????????????????????????? handler._index=handlers.length - 1;

????????????????????????? }

???????????????? } else {

????????????????????????? // 创建事件处理函数的数组

????????????????????????? el._events[type]=[];

????????????????????????? // 缓存总的事件绑定函数

????????????????????????? el._eventHandlers=function (e) {

???????????????? ???????????????? e=e || window.event;

?????????????????????????????????? var handlers=el._events[type];

?????????????????????????????????? // 依次执行所有缓存的事件处理方法

?????????????????????????????????? for (var i=0, len=handlers.length; i

??????????????????????????????????????????? typeof handlers[i]==='function' && handlers[i](e);

?????????????????????????????????? }

????????????????????????? };

????????????????????????? // 绑定一次事件,来处理所有绑定在该事件上的所有函数

????????????????????????? el.attachEvent('on' + type, el._eventHandlers);

????????????????????????? addEvent(el, type, handler);

???????????????? }

???????? } else { // 现代浏览器

???????????????? el.addEventListener(type, handler, false);

???????? }

}

相应的,移除事件的方法也要做一些处理。

function removeEvent(el, type, handler) {

???????? if (el.detachEvent) { // IE

???????????????? // 取得事件处理函数的数组的引用

???????????????? var handlers=el._events && el._events[type];

???????????????? if (handlers) {

????????????????????????? if (handler) {

?????????????????????????????????? // 删除事件处理函数队列里的函数

?????????????????????????????????? delete handlers[handler._index];

?????????????????????????????????? // 删除函数的索引

?????????????????????????????????? delete handler._index;

?????????????????????????????????? if (handlers.length===0) {

??????????????????????????????????????????? // 解除总的事件绑定

??????????????????????????????????????????? el.detachEvent('on' + type, el._eventHandlers);

?????????????????????????????????? }

????????????????????????? } else {

?????????????????????????????????? // 解除总的事件绑定

?????????????????????????????????? el.detachEvent('on' + type, el._eventHandlers);

?????????????????????????????????? // 删除该事件绑定上的所有事件处理函数队列

?????????????????????????????????? delete el._events[type];

????????????????????????? }

???????????????? }

???????? } else { // 现代浏览器

???????????????? el.removeEventListener(el, type, handler);

???????? }

}

为了实现各个浏览器下,同一事件绑定多个方法的次序能正确执行。代码上不得不多做一些处理。有兴趣的可以再细致读一下代码,如有问题,欢迎指正。即使无需用到,也需要清楚,绑定在同一事件上的多个方法之间不要有顺序依赖。

接调用(异步调异步)

function fn1() {
	setTimeout(()=> {
		console.log('fn1执行')
		fn2('fn1传递过去的参数')
	}, 1000)
}

function fn2(data) {
	setTimeout(()=> {
		console.log('fn2执行', data)
		fn3('fn2传递过去的参数')
	}, 1000)
}

function fn3(data) {
	setTimeout(()=> {
		console.log('fn3执行', data)
	}, 1000)
}
fn1()

执行结果如下:


我们发现这种方式虽然能实现,但是代码量一旦变多,可读性就降低了。

Promise的方式

function fn1() {
	return new Promise((resolve, reject)=> {
		console.log('fn1执行')
		setTimeout(()=> {
			console.log('fn1结束')
			resolve('fn1传递过去的参数')
		}, 1000)
	})
}

function fn2(data) {
	return new Promise((resolve, reject)=> {
		console.log('fn2执行,接收的参数', data)
		setTimeout(()=> {
			resolve('fn2传递过去的参数')
		}, 1000)
	})
}

function fn3(data) {
	return new Promise((resolve, reject)=> {
		console.log('fn3执行,接收的参数', data)
		setTimeout(()=> {
			resolve('fn3传递过去的参数')
		}, 1000)
	})
}

fn1().then(fn2).then(fn3).then(res=> {
	console.log('最后一个', res)
})

生成器的方式

生成器就是能返回一个迭代器的函数,它也是一个函数,对比普通的函数,多了一个*,在它的函数体内可以使用yield关键字,函数会在每个yield后暂停,等待,直到这个生成的对象,调用下一个next(),每调用一次next会往下执行一次yieId,然后暂停。

这里是云端源想IT,帮你轻松学IT”

嗨~ 今天的你过得还好吗?

我们总是先扬起尘土

然后抱怨自己看不见

- 2024.04.17 -

JavaScript是一种轻量级的编程语言,通常用于网页开发,以增强用户界面的交互性和动态性。然而在HTML中,有多种方法可以嵌入和使用JavaScript代码。

本文就带大家深入了解如何在HTML中使用JavaScript。



一、使用 script 标签

要在HTML中使用JavaScript,我们需要使用<script>标签。这个标签可以放在<head>或<body>部分,但通常我们会将其放在<body>部分的底部,以确保在执行JavaScript代码时,HTML文档已经完全加载。

使用 <script> 标签有两种方式:直接在页面中嵌入 JavaScript 代码包含外部 JavaScript 文件。


包含在 <script> 标签内的 JavaScript 代码在浏览器总按照从上至下的顺序依次解释。


所有 <script> 标签都会按照他们在 HTML 中出现的先后顺序依次被解析。



HTML 为 <script> 定义了几个属性:

1)async:可选。表示应该立即下载脚本,但不妨碍页面中其他操作。该功能只对外部 JavaScript 文件有效。


如果给一个外部引入的js文件设置了这个属性,那页面在解析代码的时候遇到这个<script>的时候,一边下载该脚本文件,一边异步加载页面其他内容。


2)defer:可选。表示脚本可以延迟到整个页面完全被解析和显示之后再执行。该属性只对外部 JavaScript 文件有效。


3)src:可选。表示包含要执行代码的外部文件。


4)type:可选。表示编写代码使用的脚本语言的内容类型,目前在客户端,type 属性值一般使用 text/javascript。不过这个属性并不是必需的,如果没有指定这个属性,则其默认值仍为text/javascript。



1.1 直接在页面中嵌入JavaScript代码

内部JavaScript是将JavaScript代码放在HTML文档的<script>标签中。这样可以将JavaScript代码与HTML代码分离,使结构更清晰,易于维护。


在使用<script>元素嵌入JavaScript代码时,只须为<script>指定type属性。然后,像下面这样把JavaScript代码直接放在元素内部即可:

<script type="text/javascript">
function sayHi(){
alert("Hi!");
}
</script>


如果没有指定script属性,则其默认值为text/javascript。


包含在<script>元素内部的JavaScript代码将被从上至下依次解释。在解释器对<script>元素内部的所有代码求值完毕以前,页面中的其余内容都不会被浏览器加载或显示。


在使用<script>嵌入JavaScript代码的过程中,当代码中出现"</script>"字符串时,由于解析嵌入式代码的规则,浏览器会认为这是结束的</script>标签。可以通过转义字符“\”写成<\/script>来解决这个问题。


1.2 包含外部 JavaScript 文件

外部JavaScript是将JavaScript代码放在单独的.js文件中,然后在HTML文档中通过<script>标签的src属性引用这个文件。这种方法可以使代码更加模块化,便于重用和共享。


如果要通过<script>元素来包含外部JavaScript文件,那么src属性就是必需的。这个属性的值是一个指向外部JavaScript文件的链接。

<script type="text/javascript" src="example.js"></script>


  • 外部文件example.js将被加载到当前页面中。
  • 外部文件只须包含通常要放在开始的<script>和结束的</script>之间的那些JavaScript代码即可。



与解析嵌入式JavaScript代码一样,在解析外部JavaScript文件(包括下载该文件)时,页面的处理也会暂时停止。

注意:带有src属性的<script>元素不应该在其<script>和</script>标签之间再包含额外的JavaScript代码。如果包含了嵌入的代码,则只会下载并执行外部脚本文件,嵌入的代码会被忽略。

通过<script>元素的src属性还可以包含来自外部域的JavaScript文件。它的src属性可以是指向当前HTML页面所在域之外的某个域中的完整URL。

<script type="text/javascript" src="http://www.somewhere.com/afile.js"></script>

于是,位于外部域中的代码也会被加载和解析。


1.3 标签的位置

在HTML中,所有的<script>标签会按照它们出现的先后顺序被解析。在不使用defer和async属性的情况下,只有当前面的<script>标签中的代码解析完成后,才会开始解析后面的<script>标签中的代码。


通常,所有的<script>标签应该放在页面的<head>标签中,这样可以将外部文件(包括CSS和JavaScript文件)的引用集中放置。



然而,如果将所有的JavaScript文件都放在<head>标签中,会导致浏览器在呈现页面内容之前必须下载、解析并执行所有JavaScript代码,这可能会造成明显的延迟,导致浏览器窗口在加载过程中出现空白。


为了避免这种延迟问题,现代Web应用程序通常会将所有的JavaScript引用放置在<body>标签中的页面内容的后面。这样做可以确保在解析JavaScript代码之前,页面的内容已经完全呈现在浏览器中,从而加快了打开网页的速度。


二、执行JavaScript 程序

JavaScript 解析过程包括两个阶段:预处理(也称预编译)执行

  • 在编译期,JavaScript 解析器将完成对 JavaScript 代码的预处理操作,把 JavaScript 代码转换成字节码;
  • 在执行期,JavaScript 解析器把字节码生成二进制机械码,并按顺序执行,完成程序设计的任务。


1、执行过程

HTML 文档在浏览器中的解析过程是:按照文档流从上到下逐步解析页面结构和信息。

JavaScript 代码作为嵌入的脚本应该也算做 HTML 文档的组成部分,所以 JavaScript 代码在装载时的执行顺序也是根据 <script> 标签出现的顺序来确定。

你是不是厌倦了一成不变的编程模式?想要突破自我,挑战新技术想要突破自我,挑战新技术?却迟迟找不到可以练手的项目实战?是不是梦想打造一个属于自己的支付系统?那么,恭喜你,云端源想免费实战直播——《微实战-使用支付宝/微信支付服务,网站在线支付功能大揭秘》正在进行,点击前往获取源码!云端源想

2、预编译

当 JavaScript 引擎解析脚本时候,他会在与编译期对所有声明的变量和函数预先进行处理。当 JavaScript 解析器执行下面脚本时不会报错。

alert(a); //返回值 undefined
var a=1;
alert(a); //返回值 1


由于变量声明是在预编译期被处理的,在执行期间对于所有的代码来说,都是可见的,但是执行上面代码,提示的值是 undefined 而不是 1。

因为变量初始化过程发生在执行期,而不是预编译期。在执行期,JavaScript 解析器是按照代码先后顺序进行解析的,如果在前面代码行中没有为变量赋值,则 JavaScript 解析器会使用默认值 undefined 。


由于第二行中为变量 a 赋值了,所以在第三行代码中会提示变量 a 的值为 1,而不是 undefined。

fun(); //调用函数,返回值1
function fun(){
alert(1);
}

函数声明前调用函数也是合法的,并能够正确解析,所以返回值是 1。但如果是下面这种方式则 JavaScript 解释器会报错。

fun(); //调用函数,返回语法错误
var fun=function(){
alert(1);
}

上面的这个例子中定义的函数仅作为值赋值给变量 fun 。在预编译期,JavaScript 解释器只能够为声明变量 fun 进行处理,而对于变量 fun 的值,只能等到执行期时按照顺序进行赋值,自然就会出现语法错误,提示找不到对象 fun。

总结:声明变量和函数可以在文档的任意位置,但是良好的习惯应该是在所有 JavaScript 代码之前声明全局变量和函数,并对变量进行初始化赋值。在函数内部也是先声明变量,后引用。

通过今天的分享,相信大家已经对JavaScript在HTML中的应用有了一定的了解。这只是冰山一角,JavaScript的潜力远不止于此。希望这篇文章能激发大家对编程的热情,让我们一起在编程的世界里探索更多的可能性!



我们下期再见!


END

文案编辑|云端学长

文案配图|云端学长

内容由:云端源想分享