探讨JavaScript语句的执行过程


Posted in Javascript onJanuary 28, 2016

废话不多说,直奔主题了。javascript的运行原理总结如下:

1、按照html文档流顺序执行javascript代码

浏览器是按照文档流从上到下逐步解析页面结构和信息的,javascript代码作为嵌入的脚本作为html文档的组成部分,所以javascript代码在加载时的执行顺序也是根据脚本标签<script>的出现顺序来确定的。

如果通过脚本标签<script>的src属性来引入外部.js文件,那么它也将按照其语句出现的顺序来执行,而且执行过程是文档加载的一部分。不会因为是外部js文件而延期执行。

2、预编译和执行顺序的关系

首先看如下这段代码:

<script type="text/javascript">
function hello() {
alert("hello");
}
hello();
function hello() {
alert("hello world");
}
hello();
</script>

上面这段js代码的输出结果是hello world 、hello world,而不是先输出hello,再输出hello world。这是因为javascript并非完全按照顺序来解释执行,而是在解释之前会对javascript进行一次预编译,在预编译的过程中,会把定义式的函数优先执行,也会把所有var变量创建,默认值为undefined,以提高程序的执行效率。也就是说上面的这段代码其实被JS引擎预编译成下面这样:

<script type="text/javascript">
var hello = function() {
alert("hello");
};
hello = function() {
alert("hello world");
};
hello();
hello();
</script>

通过上面的代码可以清晰的看到,函数其实也是变量,可以对函数进行赋值。为了防止前面那种情况的出现,可以如下定义成两个js文件:

<script type="text/javascript">
hello();
function hello() {
alert("hello");
}
// hello();
</script>
<script type="text/javascript">
function hello() {
alert("hello world");
}
hello();
</script>

上面第一个文件,我把hello()放在了function的前面,也是可以输出正确结果的。

<script type="text/javascript">
hello();
var hello = function() {
alert("hello");
};
// hello();
</script>

如果用上面的这种方法对function函数进行定义,那么就会报错,报错信息如下图1所示:

 

这里报错hello is not a funtion,这是由于在预编译的时候,对于用var声明的变量,虽然最先就处理了,但是变量值是undefined。然后运行hello()的时候,由于前面的hello是undefined,类型没有确定,所以这里是hello is not a function。虽然,程序中有定义这个函数,但是定义的位置放在了调用的后面,那么调用的时候,程序并没有运行到这里,所以没用。

再来看下面的这一段代码:

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

上面的这段代码虽然调用也是在函数定义的前面,但是这里是以function关键字来定义的,用function来定义的时候,跟var不一样,function定义的时候就已经把函数的值赋了过去,所以这里可以运行。

总结:

当javascript引擎解析脚本时,它会在预编译期对所有声明的变量和函数进行处理。处理如下:

(1)在执行前会进行类似“预编译”的操作:首先会创建一个当前执行环境下的活动对象,并将那些用var声明的变量设置为活动对象的属性,但是此时这些变量的赋值都是undefined,并将那些以function定义的函数也添加为活动对象的属性,而且它们的值正是函数的定义。

(2)在解释执行阶段,遇到变量需要解析时,会首先从当前执行环境的活动对象中查找,如果没有找到而且该执行环境的拥有者有prototype属性时则会从prototype链中查找,否则将会按照作用域链查找。遇到var a = ...这样的语句时会给相应的变量进行赋值(注意:变量的赋值是在解释执行阶段完成的,如果在这之前使用变量,它的值会是undefined)。

(3)综上,一句话总结就是:变量的声明在预编译期,变量的初始化在运行期。

<script type="text/javascript">
alert(a); // 在预编译期间a变量已经加载,但是用var定义,所以赋值为undefined先,故这里输出undefined。
var a = 1; // 这里给前面的没有赋值的a进行赋值为1
alert(a); // 这里输出的a已经是前面赋值过的,所以输出1。
</script>

对于上面的这段代码,输出结果是:先输出undefined,后输出1,分析见代码备注。

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

3、按块执行javascript代码

所谓代码块就是使用<script>标签分隔的代码段。JavaScript解释器在执行脚本时,是按块来执行的。通俗地说,就是浏览器在解析HTML文档流时,如果遇到一个<script>标签,则JavaScript解释器会等到这个代码块都加载完后,先对代码块进行预编译,然后再执行。执行完毕后,浏览器会继续解析下面的HTML文档流,同时JavaScript解释器也准备好处理下一个代码块。由于JavaScript是按块执行的,所以如果在一个JavaScript块中调用后面块中声明的变量或函数就会提示语法错误。

<script>
alert(a);
</script>
<script>
var a = 1;
</script>

上面的这段代码,由于是两个代码块,先执行完第一个代码块,再执行第二个代码块。执行第一个代码块的时候,变量a没有声明,所以报错,报错信息是:a is not defined。

<script>
var a = 1;
</script>
<script>
alert(a);
</script>

虽然说,JavaScript是按块执行的,但是不同块都属于同一个全局作用域,也就是说,块之间的变量和函数是可以共享的。所以,上面的这两个代码块运行的时候,虽然是两个代码块,但是第一段运行以后,a变量就存在了全局作用域中,此时运行到第二个代码块,输出的a变量就可以调用全局作用域中的a,所以没有问题。

4、借助事件机制改变javascript执行顺序

由于JavaScript是按块处理代码,同时又遵循HTML文档流的解析顺序,所以在上面示例中会看到这样的语法错误。但是当文档流加载完毕,如果再次访问就不会出现这样的错误。为了安全起见,我们一般在页面初始化完毕之后才允许JavaScript代码执行,这样可以避免网速对JavaScript执行的影响,同时也避开了HTML文档流对于JavaScript执行的限制。

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>javascript</title>
<script>
window.onload = function() {
alert(a);
};
</script>
<script>
var a = 1;
alert("bb");
</script>
</head>
<body>
</body>
<script>
alert("cc");
</script>
</html>

windows.onload = function()表示先在触发事件上加一个函数,并不立即执行,而是在整个页面都加载完成以后再开始执行该事件,及function。所以,在windows.onload执行之前,就已经把一些变量加载到了全局区中,所以没有问题。上面的输出结果是:先输出bb,再输出cc,最后输出a。

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>javascript</title>
<script>
window.onload = function() {
alert(a);
};
// 上面的onload不会执行,只会执行下面的onload
window.onload = function() {
alert("onload2");
};
</script>
<script>
var a = 1;
alert("bb");
</script>
</head>
<body>
</body>
<script>
alert("cc");
</script>
</html>

如果在一个页面中存在多个windows.onload事件处理函数,则只有最后一个才是有效的(如上面的代码所示),为了解决这个问题,可以把所有脚本或调用函数都放在同一个onload事件处理函数中,如下面的代码所示:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>javascript</title>
<script>
window.onload = function() {
// 放到一起
alert(a);
alert("onload2");
};
</script>
<script>
var a = 1;
alert("bb");
</script>
</head>
<body>
</body>
<script>
alert("cc");
</script>
</html>

5、javascript输出脚本的执行顺序

在JavaScript开发中,经常会使用document对象的write()方法输出JavaScript脚本。document.write()方法先把输出的脚本字符串写入到脚本所在的文档位置,浏览器在解析完document.write()所在文档内容后,继续解析document.write()输出的内容,然后才按顺序解析后面的HTML文档。也就是说,JavaScript脚本输出的代码字符串会在输出后马上被执行。请注意,使用document.write()方法输出的JavaScript脚本字符串必须放在同时被输出的<script>标签中,否则JavaScript解释器因为不能够识别这些合法的JavaScript代码,而作为普通的字符串显示在页面文档中。但是,通过document.write()方法输出脚本并执行也存在一定的风险,因为不同JavaScript引擎对其执行顺序不同,同时不同浏览器在解析时也会出现Bug。

以上所述是小编给大家介绍的JavaScript语句的执行过程,希望对大家有所帮助。

Javascript 相关文章推荐
javascript中巧用“闭包”实现程序的暂停执行功能
Apr 04 Javascript
js 函数的副作用分析
Aug 23 Javascript
xmlhttp缓存清除的2种解决方法
Dec 13 Javascript
js正则表达式匹配数字字母下划线等
Apr 14 Javascript
bootstrap网页框架的使用方法
May 10 Javascript
聊一聊JS中的prototype
Sep 29 Javascript
详解微信小程序开发之下拉刷新 上拉加载
Nov 24 Javascript
如何在Angular.JS中接收并下载PDF
Nov 26 Javascript
详解Angular2学习笔记之Html属性绑定
Jan 03 Javascript
微信网页授权并获取用户信息的方法
Jul 30 Javascript
vue+axios 前端实现登录拦截的两种方式(路由拦截、http拦截)
Oct 24 Javascript
微信小程序按顺序同步执行的两种方式
Dec 20 Javascript
Javascript复制实例详解
Jan 28 #Javascript
基于jQuery实现以手风琴方式展开和折叠导航菜单
Jan 28 #Javascript
基于JavaScript的操作系统你听说过吗?
Jan 28 #Javascript
js+canvas绘制矩形的方法
Jan 28 #Javascript
js+canvas简单绘制圆圈的方法
Jan 28 #Javascript
谈一谈javascript闭包
Jan 28 #Javascript
JavaScript统计字符串中每个字符出现次数完整实例
Jan 28 #Javascript
You might like
使用NetBeans + Xdebug调试PHP程序的方法
2011/04/12 PHP
PHP第一季视频教程(李炎恢+php100 不断更新)
2011/05/29 PHP
PHP cdata 处理(详细介绍)
2013/07/05 PHP
浅谈ThinkPHP的URL重写
2014/11/25 PHP
php 5.6版本中编写一个PHP扩展的简单示例
2015/01/20 PHP
Windows下Apache + PHP SESSION丢失的解决过程全纪录
2015/04/07 PHP
Yii2框架实现数据库常用操作总结
2017/02/08 PHP
PHP与JavaScript针对Cookie的读写、交互操作方法详解
2017/08/07 PHP
PHP使用 Pear 进行安装和卸载包的方法详解
2019/07/08 PHP
Laravel validate error处理,ajax,json示例
2019/10/25 PHP
一些易混淆且不常用的属性,希望有用
2007/01/29 Javascript
火狐浏览器(firefox)下获得Event对象以及keyCode
2008/11/13 Javascript
JS 文件传参及处理技巧分析
2010/05/13 Javascript
JS中的this变量的使用介绍
2013/10/21 Javascript
jQuery中hover方法和toggle方法使用指南
2015/02/27 Javascript
全面了解JavaScirpt 的垃圾(garbage collection)回收机制
2016/07/11 Javascript
Vue.js每天必学之表单控件绑定
2016/09/05 Javascript
微信小程序手势操作之单触摸点与多触摸点
2017/03/10 Javascript
Vue.js计算机属性computed和methods方法详解
2019/10/12 Javascript
vue 实现用户登录方式的切换功能
2020/04/14 Javascript
js实现简单抽奖功能
2020/11/24 Javascript
Python中使用不同编码读写txt文件详解
2015/05/28 Python
Python os模块学习笔记
2015/06/21 Python
Python黑帽编程 3.4 跨越VLAN详解
2016/09/28 Python
对python 各种删除文件失败的处理方式分享
2018/04/24 Python
利用Python如何实现一个小说网站雏形
2018/11/23 Python
实例讲解Python中浮点型的基本内容
2019/02/11 Python
postman模拟访问具有Session的post请求方法
2019/07/15 Python
python中导入 train_test_split提示错误的解决
2020/06/19 Python
工厂实习感言
2014/01/14 职场文书
简单租房协议书
2014/10/21 职场文书
2015年银行大堂经理工作总结
2015/04/24 职场文书
职位证明模板
2015/06/23 职场文书
2016保送生自荐信范文
2016/01/29 职场文书
php远程请求CURL案例(爬虫、保存登录状态)
2021/04/01 PHP
德劲DE1107指针试高灵敏度全波段收音机机评
2022/04/05 无线电