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声明的变量会会形成块级作用域(类似于私有作用域)
说明:也就是在代码运行之前会做语法检查
实践是检验真理的唯一标准,下一篇我会拿一些常用的题来实际解读。