- script标签的两个属性defer和async有什么区别
- 原型
- 进程和线程
1.defer和async的区别
defer和async是script标签的两个属性,用于在不阻塞文档解析的前提下,控制脚本的下载和完成。
在介绍他们之前,我们有必要先了解一下页面的加载和渲染过程:
- 浏览器通过HTTP协议请求服务器,获取到HTML文档并开始从上到下解析,构建DOM;
- 在构建DOM的过程中,若遇到外联的样式声明和脚本声明,则暂停文档解析,创建新的网络连接,并开始下载新的样式文件和脚本文件;
- 样式文件下载完成后,构建CSSDOM;脚本文件下载完成后,解释并执行,然后继续解析文档 构建DOM;
- 完成文档解析后,将DOM和CSSDOM进行关联和映射,最后将视图渲染到浏览器窗口;
在页面的加载和渲染过程中,脚本文件的下载和执行是与文档解析同步进行的,也就是说,它会阻塞文档解析,如果控制的不好,在用户体验上就会造成一定程度的影响。所以我们要清楚使用defer和aysnc来控制外部脚本的执行。
async
async: HTML5新增属性,用于异步下载脚本文件,下载完毕立即解析执行代码。这种方式是阻塞的,会造成网页空白现象。
关于async,需要注意以下几点:
- 和defer一样,只适用于外联脚本;
- 如果有多个声明了async的脚本,其下载和执行也是异步的,不能确保彼此的先后顺序;
- async会在load事件之前执行,但不能确保与DOMContentLoaded的先后执行顺序;
defer
defer: 用于开启新的线程下载脚本文件,并使脚本在文本解析完成后执行。defer下载外部脚本不是阻塞的,浏览器会另外开启一个线程,进行网络连接下载,在这个过程中,文档解析及构建DOM仍可继续进行,不会出现因下载脚本而出现的页面空白现象。
关于defer,需要注意以下几点:
- defer只适用于外联脚本,如果script标签没有指定src属性,只是内联脚本,不要使用defer;
- 如果有多个声明了defer的脚本,则会按顺序下载和执行;
- defer脚本会在DOMContentLoaded和load事件之前执行。
2.原型
原型对象
在JavaScript中,每当定义一个对象(Object Function)的时候对象中都会包含一些预定义的属性,其中函数对象的一个属性就是原型对象prototype。注意:普通对象没有prototype但有_proto_属性。
原型对象prototype主要用于继承。
var person = function(name){
this.name = name
};
person.prototype.getName = function(){
return this.name;
}
var zjh = new person(‘zhangjiahao’);
zjh.getName(); //zhangjiahao
从这个例子可以看出,通过给person.prototype设置了一个函数对象的属性,那person对象实例出来的普通对象就继承了这个属性,具体是通过原型链方式实现的继承。
原型链
JS在创建对象的时候(不论是普通对象还是函数对象),都有一个叫做_proto_的内置属性,用于指向创建它的函数对象的原型对象prototype。以上面的例子为例:
console.log(zjh.proto === person.prototype); //true
同样,person.prototype对象也有__proto__属性,它指向创建它的函数对象(Object)的prototype。
console.log(person.prototype.proto === Object.prototype) //true
继续,Object.prototype对象也有__proto__属性,但它比较特殊,为null
console.log(Object.prototype.proto) //null
我们把这个由__proto__串起来的直到Object.prototype.__proto__为null的这个链叫做原型链,如下图:
constructor
原型对象的prototype都有预定义的constructor的属性,用来引用它的函数对象,这是一种循环引用:
person.prototype.constructor === person //true;
Function.prototype.constructor === Function //true
Object.prototype.constructor === Object //true
有两点需要注意:
- Object.constructor===Function //true;本身Object就是Function函数构造出来的
- 如何查找一个对象的constructor,就是在该对象的原型链上寻找碰到的第一个constructor属性所指向的对象。
总结
- 原型和原型链是JS实现继承的一种模型。
- 原型链的形成是靠__proto__,而非prototype。
3.进程和线程
进程和线程都是由操作系统所体现的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。进程和线程的区别在于:
简而言之,一个程序至少有一个进程,一个进程至少有一个线程。
线程的划分尺度小于进程,使得多线程程序的并发性高。
另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的 运行效率。
线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口,顺序执行序列和程序的出口。但是线程 不能够独立执行,必须 依存在应用程序中,由应用程序提供多个线程执行控制。
从逻辑角度来看,多线程的意义在于一个应用程序中,有多个部分可以同时执行。但操作系统并没有将多个线程看做是多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。