js之堆、栈内存基础

函数渲染规则

全局作用域:当浏览器打开页面时,浏览器会给当前JS代码提供一个可以执行的运行环境,那么这个环境就是全局作用域。

  • 一个页面只对应一个全局作用域;在当前的全局作用域,浏览器给当前作用域提供了全局的对象叫window
  • JS是单线程的,每次只能执行一行代码。
  • 在JS中只要遇到报错,代码立即停止,不再向下执行。

全局变量:在全局作用域定义的变量都是全局变量。全局变量都会给window新增一个键值对。

私有变量:在私有作用域中定义的变量;

  • 外层作用域获取不到私有作用域下的变量;
  • 形参也是私有变量;
  • 在私有作用域下定义的也是私有变量。

闭包:函数执行会形成一个私有的作用域,保护里面的变量不受外界干扰。

在原有浏览器渲染机制下(ES6之前),基于typeof等逻辑运算符检测一个未被声明过得变量,不会报错,返回undefined;ES6版本直接报错

变量提升

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

变量提升五种特殊情况

  1. 如果有条件,不管条件是否成立,都要进行变量提升;对于function函数,在最新的版本浏览器中,只声明不定义;在旧版本浏览器中,带function的不仅声明,还要定义。

在块级作用域下,定义的函数,只要一进块级作用域,它会立即开辟空间,并且赋值。

    console.log(num);//undefined
    console.log(ef);//undefined
    if([]){
        console.log(ef);//整个函数
        var num=100;
        function ef(){

        }
    }
  1. 变量提升只发生在等号的左边。
var fn=function fn1(){

}
//等号的右边不会发生变量提升,只有执行到这一行时,右边函数才开始开辟堆内存。
  1. return下面的代码需要进行变量提升;但是如果return后面紧跟的代码(返回的值)不进行变量提升。
  2. 如果变量名重复,不需要重新声明,但是要重新定义。(不管是变量提升还是代码执行阶段都是如此)
//例1
    console.log(fn);  //函数本身
    var fn = 88;
    function fn() {
    }
    console.log(fn);//88
//例2
    fn();//3
    function fn() {//在变量提升阶段已经对这段代码解析过了,代码运行时,不再解析
        console.log(1);
    }
    fn();//3
    function fn() {
        console.log(2);
    }
    fn = 100;//变量提升阶段不提升
    function fn() {
        console.log(3);
    }
    fn();//报错
  1. 匿名函数不需要进行变量提升。当代码解析到匿名函数时,会先开辟一个堆内存,然后马上形成一个栈内存。

在ES6:ECMAScript中:
let声明变量

  1. let声明的变量不进行变量提升
  2. 在同一作用域下,let声明的变量不可以重名
  3. ES6中,块级作用域:if...else...、for、while...,唯独对象的大括号不是块级作用域

上一级作用域

当获取变量对应值时,如果当前作用域不存在,需要向上一级作用域查找。
函数的上一级作用域跟函数在哪执行没关系,跟函数在哪定义有关,函数在哪个作用域下定义,上一级作用域就是谁。

作用域链:当获取变量值时,首先会看自己私有作用域有没有,如果没有,需要向上一级作用域查找,如果上一级 也没有,需要再向上一级查找,指导找到全局作用域为止,如果全局作用域也没有,那就报错,这样一级一级向上查找形成一条作用域链。

堆内存与栈内存

在JS中,浏览器会形成两个虚拟的内存:栈内存和堆内存

堆内存

主要用来存储引用数据类型内容的。

栈内存:作用域

  1. 提供了代码的运行环境;
  2. 存储基本数据类型值。

堆内存的销毁

堆内存的回收

  • 谷歌浏览器:每隔一段时间会对引用类型的空间地址进行检测,检测当前地址有没有被占用;如果没有被占用,那么将其回收;如果占用,不能回收。
  • 火狐和IE:采用了计数的规则,当前的引用地址被占用一次,就会+1;如果不再占用,就会-1;如果当前地址被占用次数为0,那么浏览器将其立即回收。

栈内存的销毁

全局作用域的销毁

全局作用域只有当关闭页面或关闭浏览器时,才会销毁;属于不销毁的作用域。

作用域一旦销毁,那么作用域所提供的代码运行环境也会随着销毁,而且存储的基本数据类型值也会随着回收。

私有作用的销毁

  • 函数执行会产生私有作用域;
  • 函数每执行一次,都会形成一个新的栈内存;

立即销毁的作用域

一般情况下,函数执行完毕,浏览器会对其栈内存立即进行回收,这就是立即销毁的作用域。

不销毁的作用域

满足两个条件:

  1. 函数执行返回一个引用数据类型的值;
  2. 返回的引用数据类型值一定要被外界接收;
//不销毁的作用域
var total=10;
function s(){
     //s形成的作用域是不销毁的;
      var total=100;
      return function(){  //满足条件1
          //执行完,就销毁
          console.log(total);//100
      }
}
var f=s();//满足条件2
f();

不立即销毁的作用域

//不立即销毁的作用域
function s(){
     //此处s形成的作用域不立即销毁,它会等到里面小函数执行完成之后,销毁当前的作用域
      var total=100;
      return function(){ 
          console.log(total);//100
      }
}
s()();
    var num = 1;// 2
    var  obj = {
        //这不是一个作用域;
        num : 10,
        // fn的属性值是自执行函数的返回值;
        fn : (function () {
            // 自执行函数的上一级作用域是全局作用域
            // 当代码解析到这一行时;
            // 在执行自执行函数时,obj没有空间地址;
            // 当obj以键值对存储时,obj现在没有空间地址,执行了这个自执行函数;
               return function(n){
                   console.log(n+(++num))
               }
            })()// obj --> undefined
    }
    var f = obj.fn;
    f(10);// 12
    f(20);// 23
    obj.fn(30);// 34
    obj.fn(40);// 45

this

this——关键字,在JS中有特定的意义;

  1. 全局作用域下的this指向window;和window是同一个空间地址。
  2. 如果给元素的事件行为绑定函数,那么函数中的this指向当前被绑定的那个元素。
  3. 函数中的this,要看函数执行前有没有 “.”,有点的话,点前面是谁,this就指向谁,如果没有点,那么会指向window。
  4. 自执行函数中的this永远指向window。
  5. 定时器中函数的this指向window。
  6. 构造函数中的this指向当前的实例。
  7. call、apply、bind可以改变函数中的this指向。
posted @ 2018-12-28 09:40  Hero行者  阅读(735)  评论(0编辑  收藏  举报