自上而下的加载解析到document,遇到词法错误,语法错误,会跳出本段<script>标签,但是还会继续加载随后的<script>标签的JavaScript代码,如此循环,直到document加载完毕。
我是从“上海全鼎软件学院”毕业的————————
以上代码中的 slideli[0].onmouseover = function () { ... }; 应该这样理解:设置slideli[0] 的 onmouseover 属性为一个函数。 注意在JavaScript中,函数本身就是一个对象(object),所以当然能够赋给slideli[0]对象的一个属性。这个函数不是此时运行,而是鼠标移到网页的slideli[0]元素上面时被调用运行,是一个事件处理函数。鼠标移动到slideli[..]中的哪个元素上面,就是哪个元素绑定的函数被调用。
在说这个话题之前,我想先说几句题外话:最近偶然碰到有朋友问我“hoisting”的问题。即在js里所有变量的声明都是置顶的,而赋值则是在之后发生的。可以看看这个例子:
var a = 'global';。
(function () {。
alert(a);。
var a = 'local';。
})();
大家第一眼看到这个例子觉得输出结果是什么?‘global’?还是‘local’?其实都不是,输出的是undefined,不用迷惑,我的题外话就是为了讲这个东西的。
其实很简单,看一看JavaScript运行机制就会明白。我们可以把这种现象看做“预声明”。但是如果稍微深究一下,会明白得更透彻。
这里其实涉及到对象属性绑定机制。因为所有JavaScript函数都是一个对象。在函数里声明的变量可以看做这个对象的“类似属性”。对象属性的绑定在语言里是有分“早绑定”和“晚绑定”之分的。
【早绑定】
是指在实例化对象之前定义其属性和方法。解析程序时可以提前转换为机器代码。通常的强类型语言如C++,java等,都是早绑定机制的。而JavaScript不是强类型语言。它使用的是“晚绑定”机制。
【晚绑定】
是指在程序运行前,无需检查对象类型,只要检查对象是否支持特性和方法即可。可以在绑定前对对象执行大量操作而不受任何惩罚。
上面代码出现的“预声明”现象,我们大可用“晚绑定”机制来解释。在函数的作用域中,所有变量都是“晚绑定”的。 即声明是顶级的。所以上面的代码和下面的一致:
var a = 'global';。
(function () {。
var a;
alert(a);。
a = 'local';。
})();
在alert(a)之前只对a作了声明而没有赋值。所以结果可想而知。
<!-- 题外话到此结束 -->。
RT:本文要说的是,在JavaScript里,我所知道的几种定义类和对象的方式:<! -- 声明:以下内容大部分来自《JavaScript高级程序设计》,只是个人叙述方式不同而已 -- >。
【直接量方式】
使用直接量构建对象是最基础的方式,但也有很多弊端。
var Obj = new Object;。
Obj.name = 'sun';。
Obj.showName = function() {。
alert('this.name');。
我们构建了一个对象Obj,它有一个属性name,一个方法showName。但是如果我们要再构建一个类似的对象呢?难道还要再重复一遍?
NO!,我们可以用一个返回特定类型对象的工厂函数来实现。就像工厂一样,流水线的输出我们要的特定类型结果。
【工厂方式】
function createObj(name) {。
var tempObj = new Object;。
tempObj.name = name;。
tempObj.showName = function () {。
alert(this.name);。
};
return tempObj;。
var obj1 = createObj('obj_one');。
var obj2 = createObj('obj_two');。
这种工厂函数很多人是不把他当做构建对象的一种形式的。一部分原因是语义:即它并不像使用了运算符new来构建的那么正规。还有一个更大的原因,是因为这个工厂每次产出一个对象都会创建一个新函数showName(),即每个对象拥有不同的版本,但实际上他们共享的是同一个函数。
有些人把showName在工厂函数外定义,然后通过属性指向该方法,可以避开这个问题:
代码
可惜的是,这种方式让showName()这个函数看起来不像对象的一个方法。
【构造函数方式】
这种方式是为了解决上面工厂函数的第一个问题,即没有new运算符的问题。可是第二个问题它依然不能解决。我们来看看。
function Obj(name) {。
this.name = name;。
this.showName = function () {。
alert(this.name);。
}
var obj1 = new Obj('obj_one');。
var obj2 = new Obj('obj_two');。
它的好处是不用在构造函数内新建一个对象了,因为new运算符执行的时候会自动创建一个对象,并且只有通过this才能访问这个对象。所以我们可以直接通过this来对这个对象进行赋值。而且不用再return,因为this指向默认为构造函数的返回值。
同时,用了new关键字来创建我们想要的对象是不是感觉更“正式”了。
可惜,它仍然不能解决会重复生成方法函数的问题,这个情况和工厂函数一样。
【原型方式】
这种方式对比以上方式,有个很大的优势,就是它解决了方法函数会被生成多次的问题。它利用了对象的prototype属性。我们依赖原型可以重写对象实例。
var Obj = function () {}。
Obj.prototype.name = 'me';。
Obj.prototype.showName = function () {。
alert(this.name);。
var obj1 = new Obj();。
var obj2 = new Obj();。
我们依赖原型对构造函数进行重写,无论是属性还是方法都是通过原型引用的方式给新建的对象,因此都只会被创建一次。可惜的是,这种方式存在两个致命的问题:
1。没办法在构建对象的时候就写入想要的属性,因为原型在构造函数作用域外边,没办法通过传递参数的方式在对象创建的时候就写入属性值。只能在对象创建完毕后对值进行重写。
2。致命问题在于当属性指向对象时,这个对象会被多个实例所共享。考虑下面的代码:
var Obj = function () {}。
Obj.prototype.name = 'me';。
Obj.prototype.flag = new Array('A', 'B');。
Obj.prototype.showName = function () {。
alert(this.name);。
var obj1 = new Obj();。
var obj2 = new Obj();。
obj1.flag.push('C');。
alert(obj1.flag); // A,B,C。
alert(obj2.flag); //A,B,C。
是的,当flag属性指向对象时,那么实例obj1和obj2都共享它,哪怕我们仅仅改变了obj1的flag属性,但是它的改变在实例obj2中任然可见。
面对这个问题,让我们不得不想是否应该把【构造函数方式】和【原型方式】结合起来,让他们互补。。。
【构造函数和原型混合方式】
我们让属性用构造函数方式创建,方法用原型方式创建即可:
var Obj = function (name) {。
this.name = name;。
this.flag = new Array('A', 'B');。
Obj.prototype = {。
showName : function () {。
alert(this.name);。
}
var obj1 = new Obj();。
var obj2 = new Obj();。
obj1.flag.push('C');。
alert(obj1.flag); // A,B,C。
alert(obj2.flag); //A,B。
这种方式有效地结合了原型和构造函数的优势,是目前用的最多,也是副作用最少的方式。
不过,有些追求完美的家伙还不满足,因为在视觉上还没达到他们的要求,因为通过原型来创建方法的过程在视觉上还是会让人觉得它不太像实例的方法(尤其对于传统OOP语言的开发者来说。)
所以,我们可以让原型活动起来,让他也加入到构造函数里面去,好让这个构造函数在视觉上更为统一。而这一系列的过程只需用一个判断即可完成。
var Obj = function (name) {。
this.name = name;。
this.flag = new Array('A', 'B');。
if (typeof Obj._init == 'undefined') {。
Obj.prototype = {。
showName : function () {。
alert(this.name);。
}
};
Obj._init = true;。
}
如上,用_init作为一个标志来判断是否已经给原型创建了方法。如果是那么就不再执行。这样其实在本质上是没有任何变化的,方法仍是通过原型创建,唯一的区别在于这个构造函数看起来“江山统一”了。
但是这种动态原型的方式是有问题的,《JavaScript高级程序设计》里并没有深究。创建第一个对象的时候会因为prototype在对象实例化之前没来的及建起来,是根本无法访问的。所以第一个对象是无法访问原型方法的。同时这种方式在子类继承中也会有问题。
关于解决方案,我会在下一文中说明。
其实就使用方便来说的话,个人觉得是没必要做这个判断的。。。呵呵 ^_^。
1.首先点击打开浏览器的菜单【工具】,然后选择【选项】-【internet选项】;如果是IE用户,可以直接在IE图标上点击右键,选择属性进入。
2.打开Internet选项设置窗口后,点击【安全】选项卡,点击【自定义级别】。
3.在弹出的【自定义级别】窗口中,找到【脚本】选项一栏。
4.然后点击下面的【Java小程序脚本】和【活动脚本】下的【启用】
5.或者也可以将脚本栏目下的所有项都选择到【启用】,
6.设置完成后,点击窗口右下角的【确定】按钮,弹出询问是否更改时,点击【是】。
7.最后,在Internet属性窗口中,点击【应用】-【确定】关闭退出即可在浏览器中启用Javascript代码并显示特效了。
一、线程和进程基本概念
进程:操作系统分配的占有CPU资源的最小单位。拥有独立的地址空间。
线程:安排CPU执行的最小单位。同一个进程下的所有线程,共享进程的地址空间。
简单讲,计算机就像工厂,进程是个大车间,计算机内部有很多个这样的大车间。线程是工人,每一个车间里的工人至少有一个。
为什么这么画呢?有点一个挨一个的效果呢?是因为以前我就老是疑惑进程或者线程运行时到底是并行?还是串行?
其实,在单CPU或单核的情况下,宏观上无论是进程也好,线程也罢,都是并行的。而在微观下,某一个具体的时刻,他们实际上都是串行的。在多CPU或多核的情况下,才是真正意义的并行。
二、线程和进程的关系、通性
关系:进程中包含着至少一个线程。在进程创建之初,就会包含一个线程,这个线程会根据需要,调用系统库函数去创建其他线程。但需要注意的是,这些线程之间是没有层级关系的,他们之间协同完成工作。在整个进程完成工作之后,其中的线程会被销毁,释放资源。
通性:都包含三个状态,就绪、阻塞、运行。通俗的讲,阻塞就是资源未到位,等待资源中。就绪,就是资源到位了,但是CPU未到位,还在运行其他。
三、线程的好处
既然,线程和进程是存在通性的,那么为什么操作系统还要设置线程这个单位,那就说说线程的几点好处:
1、在一个程序中,多个线程可以同步或者互斥并行完成工作,简化了编程模型;
2、线程较进程来讲,更轻;
3、线程虽然微观并行。但是,在一个进程内部,一个线程阻塞后,会执行这个进程内部的其他线程,而不是整体阻塞。从某种意义上,提高了CPU的利用率。
四、市面上的通用叫法
单线程与多线程,都指在一个进程内的单和多。不要笑我,之前真的不懂。心中那只小羊驼,奔过来,跑过去。还抬起了傲娇的眼睛,看了我一眼,呵呵~~~。
五、javaScript单线程执行机制。
1、 首先解释下,单线程和多线程。
什么是单线程?单线程就是一个进程中只有一个线程。程序顺序执行,前面的执行完,才会执行后面的程序。
什么是多线程?多线程就是一个进程中只有多个线程。在进程内部进行线程间的切换,由于每个线程执行的时间片很短,所以在感觉上是并行的。
2、那么为什么感觉上javaScript是多线程?而且还支持AJAX异步呢?AJAX是真正的异步吗?
先说明,从哪里可以得出javaScript是单线程。比如你页面一上来就alert(“hello world~”);只要你不关闭这个对话框,后续的js代码就不会再执行。因为,单线程就是这样一步一步的顺次执行,前面不执行完,后面不会执行。也就是说,在具体的某一时刻,只有一段代码在执行。
可是,JavaScript明明可以处理各种触发事件,感觉上是异步多线程啊。其实,它的原理是这样的,JavaScript单线程的执行浏览器的一个事件队列,要执行的函数和触发事件的回调函数都被放在这个队列中。比如,我点击率一下按钮,之后又将浏览器缩小了,那么这两个事件的回调函数就会顺次地被放在当前执行的“函数”之后,再一一执行。
那么,既然JavaScript是单线程,那么如何维护这个函数队列呢,他分身无术啊。这时候,就需要知道,浏览器可不是单线程。虽然,每一个window只有一个js引擎,但是浏览器是事件驱动的、异步的、多线程的。
浏览器内部有一个事件轮询(event loop),是一个大的内部消息循环,会轮询大的消息队列,并执行。也就是js要处理的事件队列,是浏览器维护的。
浏览器至少有四个线程(不同浏览器会有差异): js引擎线程、界面渲染线程、浏览器事件触发线程、http请求线程。
其实,到这里就说的很明白了。但是,又想到了延时函数(setTimeout)的例子,感觉上,因为没有阻塞执行,会感觉是异步,其实并不是。只是,js在执行到延时函数时,会触发浏览器的定时器,到设置时间,浏览器再将这个函数放入执行的函数队列,再由JavaScript引擎执行。都是在浏览器空闲了才会执行。
关于AJAX的异步,是真正的异步。同样的道理,在调用AJAX的时候,浏览器会开辟一个新的线程,去处理这个请求,得到响应后,如果这个请求有回调,会将这个回调再放入事件队列中。再由JavaScript引擎执行。
3、关于JavaScript的阻塞。
浏览器虽然是多线程,但是由于JavaScript具有阻塞特性,无论外链还是内嵌脚本,在浏览器执行解释js脚本的时候,浏览器是不会去做别的事情的,比如渲染页面,而是直到js下载并执行完毕。
这样,js脚本的下载、解释执行,会反该页面的继续绘制,给用户带来不良的体验。所以,要对其优化,有如下几点:
a、将<script>内嵌和外链,在可以的情况下,放在<body>底部。注:对于css,浏览器是并行下载。
b、在页面onload后,加载js。
c、html5 <script>标签的defer属性,在页面加载完成后下载。
d、使用创建<script>标签的方式,在页面加载完成后添加进去。
注:解决阻塞就是一句话,先让页面渲染完,再加载js。
六、Node.js单线程执行机制。
脑子就像有问题一样。js都是单线程的了,node.js就是js实现的,还能是多线程?!!呵呵哒~。
Node.js其实还不是真正意义上的js,他是借用了js语法实现的,达到真正意义的非阻塞服务端语言。但是他的运行机制,也是事件轮询 (Event Loop)。