探讨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 文本框水印/占位符(watermark/placeholder)实现方法
Jan 15 Javascript
js如何获取file控件的完整路径具体实现代码
May 15 Javascript
Jquery实现由下向上展开效果的例子
Dec 08 Javascript
node.js中的http.createClient方法使用说明
Dec 15 Javascript
Javascript中innerHTML用法实例分析
Jan 12 Javascript
BootStrap入门教程(三)之响应式原理
Sep 19 Javascript
Vue.js实现分页查询功能
Nov 15 Javascript
vue中子组件向父组件传递数据的实例代码(实现加减功能)
Apr 20 Javascript
VUE2.0 ElementUI2.0表格el-table自适应高度的实现方法
Nov 28 Javascript
解决 viewer.js 动态更新图片导致无法预览的问题
May 14 Javascript
详解Vue3.0 前的 TypeScript 最佳入门实践
Jun 18 Javascript
JS实现放大镜效果
Sep 21 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
用PHP实现小型站点广告管理
2006/10/09 PHP
php数组合并array_merge()函数使用注意事项
2014/06/19 PHP
PHP中绘制图像的一些函数总结
2014/11/19 PHP
memcache一致性hash的php实现方法
2015/03/05 PHP
php处理抢购类功能的高并发请求
2018/02/08 PHP
thinkphp5引入公共部分header、footer的方法详解
2018/09/14 PHP
Javascript中indexOf()和lastIndexOf应用方法实例
2016/08/24 Javascript
有关文件上传 非ajax提交 得到后台数据问题
2016/10/12 Javascript
ng2学习笔记之bootstrap中的component使用教程
2017/03/09 Javascript
浅析JS中常用类型转换及运算符表达式
2017/07/23 Javascript
Vue2.0 axios前后端登陆拦截器(实例讲解)
2017/10/27 Javascript
微信小程序商品详情页的底部弹出框效果
2020/11/16 Javascript
jquery 通过ajax请求获取后台数据显示在表格上的方法
2018/08/08 jQuery
微信小程序时间戳转日期的详解
2019/04/30 Javascript
Layui数据表格 前后端json数据接收的方法
2019/09/19 Javascript
浅谈VUE中演示v-for为什么要加key
2020/01/16 Javascript
零基础写python爬虫之打包生成exe文件
2014/11/06 Python
在Python中使用PIL模块处理图像的教程
2015/04/29 Python
Python输入二维数组方法
2018/04/13 Python
python使用turtle库与random库绘制雪花
2018/06/22 Python
Python爬虫实现抓取京东店铺信息及下载图片功能示例
2018/08/07 Python
解决python os.mkdir创建目录失败的问题
2018/10/16 Python
matplotlib绘制多个子图(subplot)的方法
2019/12/03 Python
Python使用grequests并发发送请求的示例
2020/11/05 Python
python 对象真假值的实例(哪些视为False)
2020/12/11 Python
python 进制转换 int、bin、oct、hex的原理
2021/01/13 Python
html5教程画矩形代码分享
2013/12/04 HTML / CSS
美国女性卫生用品公司:Thinx
2017/06/30 全球购物
加拿大拼图大师:Puzzle Master
2020/12/28 全球购物
商务会议邀请函
2014/01/09 职场文书
森林防火标语
2014/06/23 职场文书
学校安全工作汇报材料
2014/08/16 职场文书
小学生感恩老师演讲稿
2014/08/28 职场文书
学校光盘行动倡议书
2015/04/28 职场文书
2015年房产销售工作总结范文
2015/05/22 职场文书
Nginx配置根据url参数重定向
2022/04/11 Servers