2020-07-13更新感想

闭包可以说是函数及其对词法环境的引用 绑在一起,构成闭包
在谷歌浏览器中如此操作可以看到:
创建一个函数fun,
在控制台查看fun.prototype.constructor中拥有[[Scopes]]内是闭包的作用域数组

var fun = function() {
     console.log(666)
}
fun.prototype // 看constructor
var fun = function() {
    var name = 'name'; 
    var funinner = function() {console.log(name)}; 
    return funinner
}

var test = fun() // 看这个test.prototype.constructor 也就是该返回的函数,在作用域中可以访问函数fun中的变量name

例1:
循环中创建闭包:

<p id="help">Helpful notes will appear here</p>
<p>E-mail: <input type="text" id="email" name="email"></p>
<p>Name: <input type="text" id="name" name="name"></p>
<p>Age: <input type="text" id="age" name="age"></p>
function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = function() {
      showHelp(item.help);
    }
  }
}

setupHelp(); 

数组 helpText 中定义了三个有用的提示信息,每一个都关联于对应的文档中的input 的 ID。通过循环这三项定义,依次为相应input添加了一个 onfocus 事件处理函数,以便显示帮助信息。

运行这段代码后,您会发现它没有达到想要的效果。无论焦点在哪个input上,显示的都是关于年龄的信息。

这三个闭包在循环中被创建,但他们共享了同一个词法作用域,在这个作用域中存在一个变量item。这是因为变量item使用var进行声明,由于变量提升,所以具有函数作用域。当onfocus的回调执行时,item.help的值被决定。由于循环在事件触发之前早已执行完毕,变量对象item(被三个闭包所共享)已经指向了helpText的最后一项。而三个闭包都共享了函数showHelp内的作用域,也就是都是用的最后一次的item.help

更改如下:

function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function makeHelpCallback(help) {
  return function() {
    showHelp(help);
  }; // 
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
  }
}

setupHelp(); 

闭包是能访问其他函数作用域内变量的函数

function name() {
    var num = 0
    var val = function() {
        return num++
    }
    return val
}

var a = name() // 返回的val

a() //在window作用域下执行

可以得到name函数内部name的值

闭包涉及到作用域和内存问题
一般我们在函数内部的局部变量会在应用完之后被垃圾回收机制回收。但是如果闭包存在,也就是说,函数内部的变量被外部全局变量调用了,全局变量存在,闭包内部的变量就不会被回收,不当的使用就会造成内存溢出的情况。

所以上面执行a()后 0 再执行a() 是1 num被一直存在内存中得不到释放,想要将其释放,a = null

这里还涉及到了匿名函数 和 箭头函数的区别 或者说是this的指向问题

var name = 'win name'
var p = {
    name: 'p name',
    getName: function() {
        return () => {
            return this.name
        }
    },
    getName2: function() {
        return function() {
            return this.name
        }
    }
    
}
p.getName()() // p name
p.getName2()() // win name

相当于
var a = p.getName()
a()this是词法作用域

var b = p.getName2()
b() 是全局作用域


chidaozhi
60 声望4 粉丝

前端老阿姨