JS面试点-闭包

什么是闭包:

  • 有权访问另外一个函数做用域的变量的函数。
  • 闭包属于一种特殊的做用域,称为静态做用域。
  • 简单的说,Javascript容许使用内部函数---即函数定义和函数表达式位于另外一个函数的函数体内,内部函数能够访问它们所在的外部函数中声明的全部局部变量、参数和声明的其余内部函数。当其中一个这样的内部函数在包含它们的外部函数以外被调用时,就会造成闭包。
    1. 闭包是嵌套的内部函数
    2. 闭包存在于嵌套的内部函数中

闭包的主要做用:

1.能够读取函数内部的变量。面试

2.让这些变量的值始终保持在内存中,变量或参数不会被垃圾回收机制回收GC。bash

产生闭包的条件:

  1. 函数嵌套
  2. 内部函数引用了外部函数的数据(变量/函数)

闭包的优势:

  1. 变量长期驻扎在内存中
  2. 避免全局变量的污染

闭包的缺点:

  1. 常驻内存会增大内存的使用量
  2. 使用不当会形成内存泄露
  3. 闭包会在父函数外部,改变父函数内部变量的值

闭包使用详解:

当想要的获得f1函数的局部变量时,正常状况下,这是办不到的,只有经过变通方法才能实现。那就是在函数的内部,再定义一个函数闭包

function f1() {
  var n = 999;
  function f2() {
  console.log(n); // 999
  }
}
复制代码

上面代码中,函数f2就在函数f1内部,这时f1内部的全部局部变量,对f2都是可见的。可是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是 JavaScript 语言特有的”链式做用域”结构(chain scope),子对象会一级一级地向上寻找全部父对象的变量。因此,父对象的全部变量,对子对象都是可见的,反之则不成立。既然f2能够读取f1的局部变量,那么只要把f2做为返回值,咱们不就能够在f1外部读取它的内部变量了吗! 
函数

function f1() {
  var n = 999;
  function f2() {
    console.log(n);
  }
  return f2;
}
var result = f1();
result(); // 999
复制代码

上面代码中,函数f1的返回值就是函数f2,因为f2能够读取f1的内部变量,因此就能够在外部得到f1的内部变量了。闭包就是函数f2,即可以读取其余函数内部变量的函数。因为在 JavaScript 语言中,只有函数内部的子函数才能读取内部变量,所以能够把闭包简单理解成“定义在一个函数内部的函数”。在本质上,闭包就是将函数内部和函数外部链接起来的一座桥梁。 
性能

闭包的最大用处有两个,一个是能够读取函数内部的变量,另外一个就是让这些变量始终保持在内存中ui

请看下面的例子,闭包使得内部变量记住上一次调用时的运算结果。this

function zxxs(zxx) {
        return function () {
            return zxx++;
        };
    }
 
    var inc = zxxs(5);
    inc() // 5
    inc() // 6
    inc() // 7
复制代码

上面代码中,zxx是函数zxxs的内部变量。经过闭包,zxx的状态被保留了,每一次调用都是在上一次调用的基础上进行计算。从中能够看到,闭包inc使得函数zxxs的内部环境,一直存在。因此,闭包能够看做是函数内部做用域的一个接口。为何会这样呢?缘由就在于inc始终在内存中,而inc的存在依赖于zxxs,所以也始终在内存中,不会在调用结束后,被垃圾回收机制回收。
spa

闭包的另外一个用处,是封装对象的私有属性和私有方法:.net

function Person (name) {
        var age
 
        function setAge (n) {
            age = n
        }
 
        function getAge () {
            return age
        }
 
        return {
            name: name,
            getAge: getAge,
            setAge: setAge
        }
    }
 
    var p1 = Person('zxx')
    p1.setAge(18)
    p1.getAge() // 18
复制代码

上面代码中,函数Person的内部变量age,经过闭包getAgesetAge,变成了返回对象p1的私有变量。
3d

注意,外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,因此内存消耗很大。所以不能滥用闭包,不然会形成网页的性能问题。

函数执行完后,函数内的局部变量没有释放,占用内存时间会变长,容易形成内存泄漏(内存被占用,可是没有用上)

function fn1 () {
    var arr = new Array[1000]
    function fn2 () {
        console.log(arr.length)
    }
    return fn2
}
var f = fn1() // 已经产生闭包
f()
 
f = null // 让内部函数成为垃圾对象-->回收闭包
复制代码

习题1(this指向详解

var name = 'The Window'
var object = {
    name: 'My Object',
    getNameFunc: function () {
        return function () {
            return this.name
        }
    }
}
alert(object.getNameFunc()())  //The Window
object.getNameFunc() // 执行完变成一个函数,this指向window, 关于this的指向后续会更新新的文章详解
var name = 'The Window'
var object = {
    name: 'My Object',
    getNameFunc: function () {
        var that = this
        return function () {
            return that.name
        }
    }
}
alert(object.getNameFunc()())  // My Object
复制代码

习题2

function outerFun(){
 var a=0;
 function innerFun() {
  a++;
  alert(a);
 }
 return innerFun;
}
var obj = outerFun();
obj();  // 结果为1
obj();  // 结果为2
var obj2 = outerFun();
obj2();  // 结果为1
obj2();  // 结果为2
复制代码

习题3

function foo (x) {
    var tmp = 3
    function bar (y) {
        console.log(x + y + (++tmp))
    }
    bar(10)
}
foo(2) // 16
foo(2) // 16
foo(2) // 16
foo(2) // 16
 
这里只是函数调用,不是闭包
========================
 
function foo (x) {
    var tmp = 3
    return function (y) {
        console.log(x + y + (++tmp))
    }
}
var bar = foo(2)
bar(10) // 16
bar(10) // 17 
bar(10) // 18
bar(10) // 19
 
当你return的是内部function时,就是一个闭包。
一个函数访问了它的外部变量,那么它就是一个闭包
复制代码

一道经典的闭包面试题

function fun(n,o){
        console.log(o);
        return {
            fun:function(m){
                return fun(m,n);
            }
        }
    }
    var a = fun(0); // undefined
    a.fun(1); // 0
    a.fun(2); // 0
    a.fun(3); // 0
    var b=fun(0).fun(1).fun(2).fun(3); //undefined,0,1,2
    var c=fun(0).fun(1); //undefined,0
    c.fun(2); // 1
    c.fun(3); // 1
复制代码

详解:

转换为等价代码
return返回的对象的fun属性对应一个新建的函数对象,
这个函数对象将造成一个闭包做用域,
使其可以访问外层函数的变量n及外层函数fun,
为了避免将fun函数和fun属性搞混,咱们将上述代码修改以下:
function _fun_(n,o){
    console.log(o);
    return {
        fun:function(m){
            return _fun_(m,n);
        }
    }
}
 
var a = fun(0); // undefined 产生了闭包 n = 0
a.fun(1); // 0 产生了闭包,可是因为没有绑定变量,闭包立刻就消失了, 始终用的是a里面的闭包	
a.fun(2); // 0 产生了闭包,可是因为没有绑定变量,闭包立刻就消失了, 始终用的是a里面的闭包 
a.fun(3); // 0 产生了闭包,可是因为没有绑定变量,闭包立刻就消失了, 始终用的是a里面的闭包
 
var b = fun(0).fun(1).fun(2).fun(3); // undefined,0,1,2 会不断产生新的闭包
等价代码
var b1 = b.fun(1);
var b2 = b1.fun(2);
var b3 = b2.fun(3);
 
var c = fun(0).fun(1); // undefined,0,
c.fun(2); // 1 当前c的闭包是 1
c.fun(3); // 1 当前c的闭包是 1
虽然c.fun(2)和c.fun(3)都产生了新的闭包,可是因为没有赋给新的变量,闭包接着就消失了
复制代码

                                          

相关文章
相关标签/搜索