最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
JavaScript虚拟入口函数Main的方法
时间:2015-09-11 编辑:简简单单 来源:一聚教程网
本文我们虚拟一个JavaScript的入口main函数作为程序的起点,这样可以让我们对JS有更深入的学习和理解。
一、JavaScript文件实际入口
JavaScript文件交给JS引擎运行时,JS引擎是从JavaScript文件的顶部由上到下逐条执行语句,直到文件尾部执行完每一条js代码。
二、JavaScript的作用域链、全局作用域和全局对象
JavaScript中的每个函数在执行时都会产生一个新的作用域。具体来说,在执行流程进入函数时会建立一个新的作用域,在函数执行完成退出时会销毁这个作用域。函数的形参、局部变量都会绑定到这个作用域里,当函数调用完成作用域销毁时,它们随之被销毁。当然在特殊情况下,如果函数返回时作用域中的某些变量仍然被引用,那么作用域以及这些被引用的变量就不会被销毁,从而形成所谓的闭包。
另一方面,我们知道函数是可以嵌套的,因而作用域也是可以嵌套的。函数在定义的时候,JS引擎会给每个函数设置一个称为[[scope]]内置属性,它指向外部函数的词法作用域。通过这种方式,多个作用域形成了链式结构,称为作用域链。通常情况下,在任意时刻只存在一条作用域链,即从正在执行的函数的作用域开始,层层上溯,直到最外层的全局作用域。
[注]:作用域链上的函数就是JS源码里的层层嵌套的函数,跟函数执行时的顺序或函数调用栈无关,这也是词法作用域这个称呼的由来。
全局作用域是一个特殊的作用域,它不是一个函数作用域,但它是所有函数作用域的外层作用域,也是所有作用域链的终点。因此只要程序没有退出,全局作用域总是存在的,全局作用域内的变量也是一直有效的。
[函数3的作用域]-->[函数2的作用域]-->[函数3的作用域]-->[全局作用域]
另外,对应于全局作用域,还有一个全局对象。在浏览器中,全局对象就是window对象。全局对象是个特殊的对象:
在全局作用域中定义的变量,都会绑定到全局对象。
在任意作用域中定义的变量,如果定义时没有用 var 关键字,都会绑定到全局对象。
在全局作用域中, this 指向全局对象。
从上面列举的这些特性可以看出,如果把全局作用域当成一个对象的话,那么实际上它就是全局对象。另外,这也解释了在全局作用域中,下面的四条语句为什么是等价的:
var a = 1;
a = 1;
window.a = 1;
this.a = 1;
三、虚构的main入口函数
既然都是作用域,为什么要有一个特殊的全局作用域呢?我们总是喜欢简单化、一致性,而尽量避免复杂化、特殊性。所以很自然地,我们会想能否让全局作用域看起来跟函数作用域没什么区别?答案是肯定的。我们可以做这样的构想:
我们想象,在JS引擎执行源文件时,会将文件中的代码包装到一个叫做main的函数中。然后把这个main函数作为程序的入口。
也就是说,假设一个JS文件中有这样的代码:
var a = 1; var b = 2; function add(x, y) { var z = x + y; return z; } console.log(add(a, b));
JS引擎在程序开始执行前会把它包装成一个main函数:
// 虚构的main函数 function main() { var a = 1; var b = 2; function add(x, y) { var z = x + y; return z; } console.log(add(a, b)); }
然后,调用这个main函数:
main._current_scope_ = window; // 将全局作用域(对象)设为window
main.call(window) // 将this指向window
四、这样做有什么意义?
(1) JS也有了入口函数main,跟其他语言一致了。
(2) 省去了全局作用域的概念,或者说全局作用域也成了函数作用域。
(3) 通过上面对main函数的调用过程,可以明白全局作用域中的那些特殊性质的由来。
(4) 最后一点,将所有JS源码当成一个函数,是为了后面讲事件队列、事件循环做铺垫。
给JavaScript程序一个统一的入口
JavaScript是一种脚本语言,浏览器下载到哪儿就会执行到那儿,这种特性给我们编程带来方便,但也很容易使得程序支离破碎,过于分散。当我们网页中需要用到的JS功能非常多时,各种JS标签零散的分布到网页里,网页加载到哪里执行到哪里,各自独立运行,虽然能满足需求,但是给我们的维护带来极大的麻烦,毫无组织性!为解决这一问题,我们为JS代码做一个统一的入口:
网页中的JavaScript从功能上,应该分为两大部分:
(1)框架部分:该部分的作用是对JavaScript代码的组织作用,包括定义全局变量和命名空间方法等。它和具体应用无关,每个页面都需要包含相同的框架。
(2)应用部分:提供页面的功能逻辑,不同页面会有不同的功能,所以不同页面应用部分的代码也不相同。
通过下面一段代码,让我们更深刻的了解一下JS代码的框架部分和应用部分:
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
在上面这段代码中,定义命名空间的方法就是我们JS代码的框架部分,这部分代码是我们每个页面中的JS代码都要用到的。功能A、B、C、D四段代码就是我们的应用部分,在不同页面实现不同的功能。
可能你已经发现,在上面这段代码中,我们与上一讲中最大的区别就是多了一个init的函数,这个函数就是我们今天主讲的入口函数了。在避免JS冲突一讲,我们不同的功能代码段是零散的分布到整个html代码中的,而这里我们将所用的功能代码统一放到init函数中。 接下来,我们需要在适当的时候调用这个入口函数(init),完成页面程序的初始化。那么什么时候调用才是最合适的时间呢?首先,我们已经知道,javascript是脚本语言,其最大的特点是加载到哪儿执行到那儿,如果程序控制某个DOM节点,而该DOM节点还没有加载,程序就会报错。如下面这段代码:
Hello ITLee
在这段代码中,正常情况下JS代码应该弹出id为demo的DOM节点的内容,但实际情况却是,在执行脚本的时候,demo节点还没有加载,所以document.getElementById(“demo”)不能找到demo节点。为了解决类似于这样的问题,我们必须保证js脚本加载的时候其所需的节点必须加载完成。
怎样才能保证在js脚本执行的时候,DOM节点都已加载完毕呢?当页面中所有的节点加载完毕后会触发onload事件,这样我们就可以监听window对象的onload事件,当window触发onload事件后才调用脚本就可以解决我们的问题。看下面这段代码:
Hello ITLee
这样,当页面加载到JS脚本时,脚本不会立即执行,而是等到window.onload之后才会去执行,这样就保证了脚本中所需的节点全部加载完毕。按照我们前面所说,应用部分的代码最好放在统一的入口函数中,那么现在完整的代码应该是这样的:
Hello ITLee
现在看来,目前我们的问题已经得到解决,但是还不算完美,因为window的onload事件要求网页内所有的元素全部加载完毕后才会触发,如果网页中有大量图片的话,加载时间会非常长,那么我们的初始化函数会延长很久才会执行,这样也是一中非常不好的用户体验方式。那么怎么解决这个问题呢?
DOMReady和window.onload的作用非常像,但是前者只判断页面内所有的DOM节点是否已经全部生成,至于节点的内容是否加载,它并不关心。所有,DOMReady有更快的触发速度,也更能满足我们的需求。需要我们注意的是,DOMReady并不是原生的Javascript支持的事件,不能像window.load那样直接调用,一般我们需要结合JS框架来使用它。下面我们以JQuery为例:
Hello ITLee
现在我们按照刚才讲的这些思路来完善我们的第一段代码,完成后的代码清单:
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx......
这样我们的JS代码就趋于完美了,所有全局作用域的函数都放在了GLOBAL命名空间下,有效控制了window作用域下函数的数量,减小了JS冲突的隐患。然后提供了一个应用部分JS的统一入口函数init,最后在DOMReady的时候调用它。
-
下一个: js中对于json判断的示例介绍
相关文章
- javascript自执行函数 03-27
- JavaScript中的函数节流的详解 11-01
- Python函数式编程 zip map filter reduce详解 08-31
- javascript只执行一次的函数详解 07-25
- JavaScript回调函数的3种用法 04-23
- javascript函数探索与总结 04-07