js原型、原型链、作用域链、闭包、垃圾回收机制

这些天看了JavaScript的一些基础,想把所学习的都总结一下,毕竟了解是一回事,表达清楚又是另一回事,所以以后会坚持把所学习的知识做一个总结,理清思路,训练表达能力,每天进步一点点(#^.^#)。

一、几个基本的概念介绍

什么是作用域?

作用域就是代码运行时,各个变量、函数及对象的可访问性,换句话说,就是作用域决定了你代码中变量和其他资源在各个区域的可见性。

什么是执行环境?

执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每个执行环境的关联了一个变量对象,这个变量对象包含了这个环境中所有的变量和函数。

我们可以理解成作为一个可执行的程序,把它抽象的理解成一个任务,当函数被调用时就立刻创建执行环境,然后被压入环境栈中执行。

什么是作用域链?

当代码在一个环境中执行时,就会创建变量对象的一个作用域链,这个作用域链保证了执行环境对有权访问的变量和函数有序访问

作用域链是向上访问的,它的最前端是当前执行环境的变量对象,接着向上访问,是其包含环境(外部环境)的变量对象,以此访问下去,最后是全局环境的变量对象。

实际上,作用域链本质上是一个指向变量对象的指针列表,它只引用而不包含实际变量对象。

什么是闭包?

在我的理解中,闭包的本质,就是将函数内部和函数外部连接起来的一座桥梁。

简单点来说,就是内部函数能够访问外部函数的变量,即使外部函数已经执行完毕。

闭包的使用场景?

因为JavaScript中只存在全局作用域和局部作用域,没有块级作用域(当然,在ES6中 let,const支持块级作用域了),那么在某些情况下,我们需要在外部获取局部变量怎么办呢,这就涉及到闭包的用处了:利用闭包可以在函数外部读取函数内部的变量,并且可以让这些变量一直保存在内存中

请看下面的例子:

function f1(){
       //f1将f2作为它的内部函数,并将其返回
    var n = 1;

      sum = function(){n+=1}

    function f2(){
      console.log(n); 
    }
    return f2;
  }
   //调用f1时返回函数f2,此时函数f1已经执行完毕
   //但此时函数f1的执行环境并不会被回收,因为在函数f2中还保持对外部函数f1中的变量n的引用
  var result = f1();

  result();//输出1  

   //变量n依然存在内存中
  sum();
  result(); //输出2

闭包的包含函数执行完后,其执行环境的作用域链会被销毁,但执行环境中的变量还存在于内存中,因为作用域链指向的是变量的引用。

在其他编程语言中,可以通过protect,public,private来设置类中变量和方法的可见性,那么在JavaScript中要怎么实现公有作用域和私有作用域呢?我们可以使用闭包来实现这一特性:利用闭包实现公有作用域和私有作用域

在下面的例子中,用一个对象中的公有作用域和私有作用域来划分函数,将公有函数return,私有函数并没有暴露出去,但是可以公有函数可以访问私有函数:

var Module = (function() {

   //私有函数
    function privateMethod() {

        console.log(1);

    }

    return {

        //公有函数
        publicMethod: function() {

            console.log(2);

            privateMethod();

        }

    };

})();

使用闭包的有什么需要注意的地方?

由于闭包能让函数中的变量一直保存在内存中,会造成内存消耗,滥用闭包会造成网页的性能问题,可能会导致内存泄漏。

二、原型链

在理解原型链之前,我们首先需要了解的是:每个函数都是一个对象,而对象又是由函数创建的

function f() { };
//函数也是一个对象,下面会打印true
console.log(f instanceof Object);
//对象是由函数创建的,下面会打印function
console.log(typeof (Object));

其次是原型和隐式原型

原型:每一个函数都有一个prototype属性,这个属性的属性值是一个对象,这个对象默认有一个constructor属性,其属性值又指向这个函数本身。

隐式原型:每个对象都有一个隐藏的属性__proto__,这个属性引用了创建这个对象的函数的prototype

function f(){} 
var f1 = new f();
console.log(f1.__proto__ === f.prototype);//true

然后需要提到的是,所有的对象都是由Object()函数创建的,所有的函数都是由Function()函数创建的,Function()函数由自身创建。

接下来上图:

js原型、原型链、作用域链、闭包、垃圾回收机制_第1张图片

 

三、垃圾回收机制

垃圾回收机制原理:找出那些不再继续使用的变量,然后释放它们占用的内存,垃圾收集器会按照固定的时间间隔,周期性的执行这一操作。

垃圾回收的算法

  • 标记清除:给当前不再使用的变量加上标记,再回收其内存
  • 引用计数:跟踪所有值被引用的次数(JavaScript引擎已不再使用这种算法,在循环引用中会导致问题)

内存泄漏:不再用到的内存,没有及时释放

解除引用:将不再使用的变量设置为null

我们应该及时解除对不再使用的变量(全局变量、全局变量属性已经循环引用变量)的引用

常见的导致内存泄漏的情况

  • 意外的全局变量,全局变量是无法进行垃圾回收的(除非解除引用,设置为null)
    
    function foo(arg) {
        bar = "some text";//相当于window.bar
    }
    

    解决方式:可以在js文件开头使用 'use strict' 严格模式,在严格模式下可以防止意外的全局变量

  • 闭包,旧版本IE中难以处理的循环引用问题,当一个循环中同时包含DOM和JavaScript对象是,IE无法释放任何一个对象。
    function assignHandler(){ 
    
    var element=document.getElementById("someElement");
    
    element.onclick=function(){
    
     return false;
    
    }
    
    }
    

    在上面的代码中,从表面上看,没有任何循环引用,但是存在闭包,内部函数有权访问外部函数的变量,DOM元素的属性onclick指向内部函数的引用,内部函数又与element之间存在隐藏的关联(作用域链),形成了一个循环引用。

       解决方式:手动设置DOM变量为null

function assignHandler(){ 

var element=document.getElementById("someElement");

element.onclick=function(){

 return false;

}

element.onclick = null;

}

 

你可能感兴趣的:(JavaScript)