深入理解JavaScript特性
最近在读《JavaScript高级程序设计》这一本书,里面提到了JavaScript的特征,倍感兴趣,于是结合自己的认识,在这里进行一下总结。
1、JavaScript的垃圾回收机制
javaScript中的5种数据类型存放在栈中(Undefined、Null、Boolean、Number、String),非基本数据类型存放在堆中,占用内存,堆不会被程序自动释放。
一张图看懂JS中数据类型的存放位置:基本数据类型存放在栈中,非基本数据类型(对象)存放在堆中,变量obi1在栈中存储的只是堆中对象的地址。
声明变量a=对象,只是声明一个指针地址指向存储该对象的内存空间,每多一个指向同一内存空间的指针地址,该对象的引用计数就会加1。
如果代码在运行过程中有一个对象不再被需要了,但是引用计数不为零,垃圾回收机制就不能释放这块内存,这就被称为内存泄漏。
释放内存的方法就是 接触对该内存地址的引用,将引用他的指针地址设置为null,如:a=null;
2、JavaScript 单线程(同一个时间只能做一件事)核心特征
JS除了主线程之外还存在一个任务队列,(js单线程所有任务必须排队完成)。
js计算能力很弱如果存在一个耗时较长的任务,后一个任务就必须等待很长时间让前一个任务执行完毕,,如果是网络请求他的耗时完全取决于网络状况,(Ajax)不符合js设计逻辑。
主线程可以忽略io(input/output)设备,挂起处于等待中的任务(监听事件绑定的任务,ajax网络请求)先运行排在后面的任务,,等到io设备有了返回结果,再回过头把挂起任务执行下去 (异步事件)。
所有任务分两种synchronous同步事件asynchronous异步事件,在js中:
同步事件在主线程上排队执行(前一个执行完毕,后一个才能执行)。
异步事件不进入主线程,而进入任务队列(task queue),只有任务任务队列通知主线程,某个异步任务可以执行了,该任务才会从任务队列中取出进入主线程。
3、JavaScript运行机制步骤
(1)所有同步任务都在主线程上依次执行,形成(执行栈)。栈的定义:先进后出,后进先出。
举个例子:定义三个函数,sayHello函数直接在student函数体内调用,isAdult函数作为参数传给了sayHello函数,在这里isAdult也是一个回调函数。
三个函数进入执行栈的顺序依次是:student,sayHello,isAdult
function student(){ var age = 18; var name = '小李' sayHello(name,age,isAdult(age)) console.log("student函数") //最后出栈 } function isAdult(age){ var result = age >= 18 ? '成年':'未成年' console.log("isAdult函数") //最先出栈 return result; } function sayHello(name,age,reust){ console.log('hello!This is '+name+' '+age+'years old 我' + reust) console.log("sayHello函数") }
student();
看输出结果:三个函数出栈的顺序依次是:isAdult,sayHello,student
(2)主线程之外,存在一个任务队列,只要异步任务有了返回结果,该任务就会被放入任务队列中。
(3)执行栈中所有任务执行完毕后,就会访问任务队列,检查队列中有哪些事件,获取任务队列中最先等待的事件,让该事件进入执行栈,开始执行。
(4)不断重复第三步。
4、JavaScript中回调函数callback(事件绑定函数)
例:绑定点击事件,当点击事件触发时 js会调用事件绑定的函数该函数称为事件的回调函数(异步任务必须指定回调函数)。
任务队列 先进先出数据结构,排在前面的事件会先被主线程读取,所以如果队列中有'定时器',定时器未必准时触发,取决于前一个任务队列中的函数执行的结束。
5、JavaScript中内存区域
程序运行时,需要内存空间存放数据,分别为stack(栈)和heap(堆)。
栈是有结构的,每个区块按照一定的顺序取存放,可以明确的知道每个区块的大小,而堆是没有结构的,数据任意存放,因此栈的调用要快于堆。
每个线程分配一个栈,每个进程分配一个堆,stack是线程独占,heap是线程公用的。
栈是系统自动开辟的自动释放的, 堆是程序员手动开辟和释放的。