JavaScript中的数据储存(执行上下文EC、栈空间ECS、堆空间)
摘要
本篇学习总结简要概述JavaScript语言代码预解析与执行的过程,以及在这过程中内存的分配与释放。
本文通过浏览器对JS代码在代码段内与代码段间的执行特点,引出代码执行的过程,运用《蛋与杯子的存放和取出》的通俗而又生动的比喻,归纳总结代码从被浏览器预解析,形成执行上下文EC==>入栈==>执行==>出栈(释放内存)的主要路线,以及在执行阶段执行上下文的内数据的指向、作用域对数据的限制特点、作用域链发挥的用处。最后运用大量习题反复巩固知识点,形成系统知识体系,并引出闭包和原型链知识点的思考。
关键字:JavaScript、代码段、预解析、行上下文EC、作用域、作用域链。
一、JavaScript代码段的概念
1. 什么是代码段:
在JavaScript(下面一律称作JS)中,一对<script> </script>
标签之间就是一个代码段。浏览器对一个代码段内JS代码预解析完成后,按从上到下的顺序执行代码。
2. 代码段内部代码执行的特点:
在一个代码段内,代码出现错误,浏览器就会在控制台(Console),打印出错误类型及错误位置,并停止错误位置以下的代码执行,开始下一个代码段内代码的预解析与执行。
3. 代码段间代码执行的特点:
每一个代码段之间是彼此独立的,浏览器在执行代码时,如果上面的代码段报错,浏览器会跳到下一段代码,上一段代码不会影响下面的代码段的执行。
4. 一个页面中可以有多个代码段
<script>
var a = 110;
console.log(a);//110
// 使用了没有声明的变量
console.log(c);
// 报错:ReferenceError 引用错误
// 同一个代码码段如报了引用错误,这个代码段的错误位置以下的代码停止执行
console.log(a);//不执行
</script>
<!-- 一张页面中可以有多个代码段 -->
<script>
var b = 220;
console.log(b);
// 上面的代码段中定义数,在下面的代码中可以执行
console.log(a);
</script>
控制台打印结果:
二、浏览器中JavaScript语言的执行
在代码段内,JS代码不是随意访问的,而是遵循严格的规则:全局作用域下的代码可以在代码段内被全局访问,局部作用域下的代码只能在本作用域内访问。
在浏览器中,JS代码执行过程可分解为两个阶段:预解析阶段(也称:预编译阶段)、代码执行阶段。
浏览器预解析完(声明提升),马上执行代码(赋值或赋值或者赋地址),是一个连续的过程。
2.1 浏览器对JS代码预解析时都做了些什么?预解析与代码执行的过程具体是怎样的?
1. 声明提升:
加var的变量 提升的是声明,没有赋值; function声明的函数要整体提升到代码段的最前面。
<script>
// 预解析提升:
// 相当于var a ;
// 相当于 function fn() {console.log('我是一个函数');}
//执行代码:
console.log(a); //undefined 预解析并未提升变量值,所以返回undefined
var a = 110;//执行到此步 相当于给 变量a赋值110;
console.log(a);//110
fn(); //'我是一个函数'
//浏览器预解析时,函数整体都提升到了最前面
function fn() {
console.log('我是一个函数');
}
</script>
控制台打印结果:
将函数体赋值给变量的情况
<script>
// 相当于 var g = undefined;
g(); //Uncaught TypeError: g is not a function 输入错误
// 加var的变量仅提升声明 不提升赋值,所以此变量的值一开始是undefined
// g 此时是值为undefined的变量 而不是一个函数function ,所以不可以用g()调用
// 函数表达式
// 本质是一个变量, var 用来声明变量
// 这个变量的值是函数 函数也是一种数据
var g = function() {
console.log('g....');
}
</script>
控制台打印结果:
2. 声明提升与执行的具体过程:
全局变量提升到页面window最上面, 如果在函数内部的(var声明)局部变量,就提升到函数内部的最前面。
<script>
// 1、最外层预解析提升:
// var a ;
// 2、函数整体提升与内部预解析:
// function fn() {
// var b ;
// c = 111;
// console.log(b);
// console.log('我是一个函数');}
//3、执行代码:
console.log(a); //undefined 预解析并未提升变量值,所以返回undefined
var a = 110; //执行到此步 相当于给 变量a赋值110
console.log(a); //110
fn();
//浏览器预解析时,函数整体都提升到了最前面
function fn() {
console.log(b); //undefined
var b = 220; //执行到此步 相当于给 变量b赋值220
c = 111; //不是var 声明的全局变量
console.log(b);
console.log('我是一个函数');
}
console.log(c); //111
</script>
控制台打印结果:
3. 特别注意:
加 var 的变量,仅仅是提升声明;函数提升的不仅只有声明,还有赋值。
三、数据与数据储存(执行的JS代码数据在内存区的分配)
3.1 数据的容器 栈(stack)和 堆(heap)
JS代码是运行在计算机(或者手机等)硬件设备上的,以计算机来说,计算机储存分为RAM内存和ROM外存(也叫硬盘),计算机的RAM内存相当于一个中转站,计算机所需调取数据是从RAM内存中调取,RAM内存的特点是数据传输速度快,而RAM内存的数据又是从ROM外存中调取的,本次重点理解RAM内存中的两个区 栈(stack)、堆(heap)。
3.2 数据的分类
数据可以分为两类:原始类型(也叫:简单数据类型、基本数据类型)、