天天看点

javascript 栈内存、堆内存、作用域、变量(变量提升ES5)全面了解之间的关系(1)

javascript 栈内存、堆内存、作用域、变量(变量提升ES5)确实是js中比较难懂的只是点。

javascript 中有三座大山,学习起来很是吃力。

第一座: 作用域和上下文环境

第二座: 原型和原型链

第三座:单线程和异步,

今天我带大家翻越这第一座大山。希望本文能让你对作用域和变量提升有个新的认识

在这之前

一.先普及下基本知识和下文需要用到的知识点

console.log(window.a) //undefined 
//以上判断无法确定a是不是window 的属性
//所以可以通过 console.log("a" in window) in 的方式来检查一个属性是不是属于该对象
           
VAR a = b = c = 1
//等于
var a = 1;
b = 1;
c = 1
           
var a, b, c = 1
//等于 
var a;
var b;
var c = 1;
           
//先++和后++的区别
var i = 10;
console.log(i++) //10
console.log(++i) //12 
           
//基本数据类型和引用数据类型的区别
var a = 12;
var b = a;
b = 13;
console.log(a); // 12

var arr = [100, 23]
var brr = arr;
brr = [100, 200]
console.log(arr); // [100, 200]

// 总结:
// 对象的定义:
// 1. 开辟一个堆内存
// 2. 把键值对存储到这个堆内存中
// 3. 把这个空间地址赋值给变量名;
           
//null 和 undefined都表示没有
    // null 表示空对象指针;

    // null 的几种情况
    // 1. 通过id获取元素如果获取不到内容,那么默认返回值是null;
    // 2. 如果需要销毁对象的堆内存,那么给对象赋值为null;
    // 3. 正则进行捕获,捕获不到内容,那么返回值是null;

    // undefined 的几种情况
    // 1. 如果变量只声明,没有赋值,那么默认存储的值就是undefined;
    // 2. 如果获取对象的属性名对应的属性值,如果属性名不存在,获取的值是undefined;
    // 3. 如果函数的形参没有对应的实参,那么形参默认存储的值是undefined
    // 4. 如果函数没有return,那么默认的返回值也是undefined;
	// typeof a //undefined (如果a没有被声明 就会产生 暂行性死区)
           

关于null 和 undefined 深入了解可以看我之前的文章:

https://blog.csdn.net/wulove52/article/details/84964313

如果上面的东西你都有所了解了,那么们接着往下看。

二. 先整体列出一些关键词,看看大家了解多少。然后我们在一个个关键词的进行说明

基本数据类型、引用数据类型、栈内存、堆内存、作用域 全局作用域 私有作用域 块级作用域(ES6私有作用域的一种)、作用域链、全局变量、私有变量、变量提升、函数执行过程、暂时性死区

JS中的内存 分为堆内存 和栈内存

栈内存:提供JS代码执行的环境和存储基本类型值

堆内存:存储引用数据类型值(对象:键值对 函数:代码字符串)

栈内存释放:

    一般情况下,当函数执行完成,所形成的私有作用域(栈内存)都会自动释放掉

    (在栈内存中存储的值也都会释放掉)

    但是也有特殊情况不销毁的情况:

    1.函数执行完成,当前形成的栈内存中,某些内容被栈内存一歪的变量占用了,此时栈内存不能释放(一旦释放

    外面找不到原有的内容了)

    2.全局栈内存只有在页面关闭的时候才会被释放掉

    如果当前栈内存没有被释放,那么之前在栈内存中存储的基本值也不会被释放,能够一直保持下来

堆内存释放

    让所有引用堆内存空间地址的变量赋值为null即可(没有变量占用,流览器会在空闲时候把它释放掉)

作用域:就是指在栈内存中形成的代码运行环境

作用域分为 全局作用域 和 私有有作用域 和 块级作用域(ES6)

全局作用域

定义:当代码运行,那么浏览器会给代码提供一个全局的运行环境,那么这个环境就是全局作用域

特点:

1.一个页面只有一个全局作用域

2. window : window是全局作用域最大的一个对象;

销毁情况:

1.页面关闭或刷新

私有作用域

定义:函数执行会形成一个新的私有的作用域

特点:

    1.私有作用域在全局作用域中形成,具有包含的关系;

    2.在一个全局作用域中,可以有很多个私有作用域

销毁情况:

1.函数执行完毕就销毁 fn()

2.函数执行完毕暂时不销毁 fn()()

3.函数执行完成不销毁

  1)函数返回一个对象

  2)并且返回的对象被其他变量引用

块级作用域 (ES6)

定义:由特定代码块形成一个作用域

特点:

    1.块级作用域是es6中新引入的一种作用域;

    2.JS中常见到的if{}  for{}  while {}  try{} catch{}  switch  case{} 都是块级作用域

    3.var obj = {};// 这不是一个块级作用域;

    4.也具有私有作用域的特点,是私有作用域的一种

作用域链:

定义:如果当前作用没有声明就会向上级查找没找到之前,一直向上查找知道到全局作用域下,这样的查找轨迹叫做作用域链

注意:

1.在私有作用下没有通过var 但是被赋值了。在作用域链中查到到全局也没有找到该变量。那么该变量会提升为全局变量

2.一个函数的上级作用域是谁,和他在哪里执行没有关系和在哪里创建(定义)的有关系,在哪里创建的,他的上级作用域就是谁

全局变量:

定义:在全局作用域下可以使用的变量就是全局变量

特点:

    1.在私有作用域中是可以获取到全局变量的,但是在全局作用域中不能获取私有变量的;

    2.如果是全局变量,都会给window新增一个键值对;属性名就是变量名,属性值就是变量名所存储的值;

    3.如果变量只被var过,那么存储值是undefined;

    4.在私有作用下没有通过var 但是被赋值了。在作用域链中查到到全局也没有找到该变量。那么该变量会提升为全局变量

私有变量:

定义:定义:私用作用域下定义的变量就是私有变量;

特点:外层作用域是获取不到私有作用域下的变量

哪些情况属性私有变量:

      1.私有作用域的实参,

      2.私有作用域下用var 声明的变量

      3.私有作用域下声明的函数function

变量提升:

定义:在当前作用域下,把带var和function进行提前的声明(提前通知有这个变量),带var的只声明不定义,带function的不仅声明而且还要定义(赋值)

特点:

    1.浏览器会过滤整个文件中代码,把带var和function给筛选出来;把筛选出变量放到当前作用域的最上端;

    2.如果变量只声明,没有定义,那么默认存储值是undefined;

    3.函数的定义是发生在变量提升阶段;

    4.变量提升只发生在当前作用域

变量提升的几种情况:

   1.不管条件是否成立,都要进行变量提升;

        1). 在最新版本浏览器中,在块级作用中,不管条件是否成立,带function只声明不定义;

        2) : 在老版本浏览器中,不仅声明而且定义;

    2. 变量提升只发生在等号的左边;

    3. 函数体中return后面的内容不需要变量提升,但是return下面的内容需要变量提升;

    4.如果变量名重复,不再进行重复声明,但要重新定义;

    5.自执行函数不进行变量提升

    6.let const

        1)let 和const都是es6新增的定义变量的语法;

        2)let和const定义的变量是不进行变量提升的;

        3)let 声明的变量不可以重名;

        4) const : 定义一个常量;

函数的执行过程:

1.形成私有作用域(运行环境 栈内存)

2.形参赋值

3.变量提升(被var 的变量和函数function)

4.代码按顺序执行

5.作用域是否销毁

闭包

函数执行形成一个私有的作用域,保护里面的私有变量不受外界干扰,这种保护机制称之为闭包

市面上:形成不销毁的私有作用域(私有栈内存)才是闭包

闭包的作用:

1.闭包具有“保护”作用

2.闭包具有“保存”作用

暂时性死区

typeof a  //undefined 在原有浏览器渲染机制下,基于typeof等逻辑运算符检测一个未被声明的变量 ,不会报错

如果是用let 声明的变量用typeof检测 就会报错,这就解决了浏览器暂行性死区问题

带var和不带var的区别

1.不带var不进行变量提升

2.不带var的本质就是window的一个属性

ES6中的let

1.在ES6中基于let/const声明的变量,不存在变量提升

2.不会和window属性映射

3.在相同作用域,如果是let声明的变量不能提前使用。也不能重复声明

4.在相同作用域,如果是let声明的变量。就不能再用var声明了

5.let声明的变量会会形成块级作用域(类似于私有作用域)

说明:也就是在代码运行之前会做语法检查

实践是检验真理的唯一标准,下一篇我会拿一些常用的题来实际解读。

继续阅读