1. "闭包就是跨作用域访问变量。"

【示例一】

var name = 'wangxi'
function user () {
// var name = 'wangxi'
function getName () {
console.log(name)
}
getName()
}
user() // wangxi

在 getName 函数中获取 name,首先在 getName 函数的作用域中查找 name,未找到,进而在 user 函数的作用域中查找,同样未找到,继续向上回溯,发现在全局作用域中存在 name,因此获取 name 值并打印。这里很好理解,即变量都存在在指定的作用域中,如果在当前作用中找不到想要的变量,则通过作用域链向在父作用域中继续查找,直到找到第一个同名的变量为止(或找不到,抛出 ReferenceError 错误)。这是 js 中作用域链的概念,即子作用域可以根据作用域链访问父作用域中的变量,那如果相反呢,在父作用域想访问子作用域中的变量呢?——这就需要通过闭包来实现。

【示例二】

function user () {
var name = 'wangxi'
return function getName () {
return name
}
}
var userName = user()()
console.log(userName) // wangxi

分析代码我们知道,name 是存在于 user 函数作用域内的局部变量,正常情况下,在外部作用域(这里是全局)中是无法访问到 name 变量的,但是通过闭包(返回一个包含变量的函数,这里是 getName 函数),可以实现跨作用域访问变量了(外部访问内部)。因此上面的这种说法完整的应该理解为:

闭包就是跨作用域访问变量 —— 内部作用域可以保持对外部作用域中变量的引用从而使得(更)外部作用域可以访问内部作用域中的变量。(还是不理解的话看下一条分析)

2. "闭包:在爷爷的环境中执行了爸爸,爸爸中返回了孙子,本来爸爸被执行完了,爸爸的环境应该被清除掉,但是孙子引用了爸爸的环境,导致爸爸释放不了。这一坨就是闭包。简单来讲,闭包就是一个引用了父环境的对象,并且从父环境中返回到更高层的环境中的一个对象。"

这个怎么理解呢?首先看下方代码:

【示例三】

function user () {
var name = 'wangxi'
return name
}
var userName = user()
console.log(userName) // wangxi

问:这是闭包吗?

答:当然不是。首先要明白闭包是什么。虽然这里形式上看好像也是在全局作用域下访问了 user 函数内的局部变量 name,但是问题是,user 执行完,name 也随之被销毁了,即函数内的局部变量的生命周期仅存在于函数的声明周期内,函数被销毁,函数内的变量也自动被销毁。

但是使用闭包就相反,函数执行完,生命周期结束,但是通过闭包引用的外层作用域内的变量依然存在,并且将一直存在,直到执行闭包的的作用域被销毁,这里的局部变量才会被销毁(如果在全局环境下引用了闭包,则只有在全局环境被销毁,比如程序结束、浏览器关闭等行为时才会销毁闭包引用的作用域)。因此为了避免闭包造成的内存损耗,建议在使用闭包后手动销毁。还是上面示例二的例子,稍作修改:

【示例四】

function user () {
var name = 'wangxi'
return function getName () {
return name
}
}
var userName = user()() // userName 变量中始终保持着对 name 的引用
console.log(userName) // wangxi
userName = null // 销毁闭包,释放内存

【为什么 user()() 是两个括号:执行 user()  返回的是 getName 函数,要想获得 name 变量,需要对返回的 getName 函数执行一次,所以是 user()()】

根据观点2,分析一下代码:在全局作用域下创建了 userName 变量(爷爷),保存了对 user 函数最终返回结果的引用(即局部变量 name 的值),执行 user()()(爸爸),返回了 name(孙子),正常情况下,在执行了 user()() 之后,user 的环境(爸爸)应该被清除掉,但是因为返回的结果 name(孙子)引用了爸爸的环境(因为 name 本来就是存在于 user 的作用域内的),导致 user 的环境无法被释放(会造成内存损耗)。

那么【"闭包就是一个引用了父环境的对象,并且从父环境中返回到更高层的环境中的一个对象。"】如何理解?

我们换个说法:如果一个函数引用了父环境中的对象,并且在这个函数中把这个对象返回到了更高层的环境中,那么,这个函数就是闭包。

还是看上面的例子:

getName 函数中引用了 user(父)环境中的对象(变量 name),并且在函数中把 name 变量返回到了全局环境(更高层的环境)中,因此,getName 就是闭包。

3. "JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里."

这句话对闭包中对变量的引用的理解很有帮助。我们看下面的例子:

var name = 'Schopenhauer'
function getName () {
console.log(name)
}
function myName () {
var name = 'wangxi'
getName()
}
myName() // Schopenhauer

如果执行 myName() 输出的结果和你想象的不一样,你就要再回去看看上面说的这句话了,

JavaScript 中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里

执行 myName,函数内部执行了 getName,而 getName 是在全局环境下定义的,因此尽管在 myName 中定义了变量 name,对getName 的执行并无影响,getName 中打印的依然是全局作用域下的 name。

我们稍微改一下代码:

 var name = 'Schopenhauer'
function getName () {
  var name = 'Aristotle'
var intro = function() { // 这是一个闭包
console.log('I am ' + name)
}
return intro
}
function showMyName () {
var name = 'wangxi'
var myName = getName()
myName()
}
showMyName() // I am Aristotle

  

结果和你想象的一样吗?结果留作聪明的你自己分析~

以上就是对 js 中闭包的理解,如果有误,欢迎指正。最后引用一段知乎问题下关于闭包概念的一个回答。

什么是闭包?

简单来说,闭包是指可以访问另一个函数作用域变量的函数,一般是定义在外层函数中的内层函数。

为什么需要闭包?

局部变量无法共享和长久的保存,而全局变量可能造成变量污染,所以我们希望有一种机制既可以长久的保存变量又不会造成全局污染。

特点

  • 占用更多内存
  • 不容易被释放

何时使用?

变量既想反复使用,又想避免全局污染

如何使用?

  1. 定义外层函数,封装被保护的局部变量。
  2. 定义内层函数,执行对外部函数变量的操作。
  3. 外层函数返回内层函数的对象,并且外层函数被调用,结果保存在一个全局的变量中。

以下为常用发送短信的闭包实现:

function sms() {
var count = 60;
return {
start: function() {
if(count == 0) {
count = 60;
document.getElementById("time").innerHTML = count;
return;
} else {
document.getElementById("time").innerHTML = count;
count--;
}
var that = this;
setTimeout(function() {
that.start();
}, 1000)
},
stop: function() {
count = 60;
}
}
}
var sms = sms();
sms.start();

  

本文摘自:http://www.cnblogs.com/wx1993/p/7717717.html?utm_source=tuicool&utm_medium=referral

js基础(闭包)的更多相关文章

  1. js基础闭包练习题

    题目描述 实现函数 makeClosures,调用之后满足如下条件:1.返回一个函数数组 result,长度与 arr 相同2.运行 result 中第 i 个函数,即 result[i](),结果与 ...

  2. JavaScript基础—闭包,事件

    Js基础-闭包,事件 1:js中的闭包 概念:在一个函数内部又定义了一个函数,内部函数能访问到外部函数作用域范围内的变量,这时这个内部函数就叫做闭包,无论这个内部函数在哪里被调用都能访问到外部函数作用 ...

  3. JS基础学习——闭包

    JS基础学习--闭包 什么是闭包 闭包的定义如下,它的意思是闭包使得函数可以记住和访问它的词法范围,即使函数是在它声明的词法范围外执行.更简单来讲,函数为了自己能够正确执行,它对自己的词法范围产生闭包 ...

  4. JS基础篇之作用域、执行上下文、this、闭包

    前言:JS 的作用域.执行上下文.this.闭包是老生常谈的话题,也是新手比较懵懂的知识点.当然即便你作为老手,也未必真的能理解透彻这些概念. 一.作用域和执行上下文 作用域: js中的作用域是词法作 ...

  5. js 基础

    js基础知识点总结 如何在一个网站或者一个页面,去书写你的js代码:1.js的分层(功能):jquery(tool) 组件(ui) 应用(app),mvc(backboneJs)2.js的规划():避 ...

  6. JS基础知识总结

      js基础知识点总结 如何在一个网站或者一个页面,去书写你的js代码:1.js的分层(功能):jquery(tool) 组件(ui) 应用(app),mvc(backboneJs)2.js的规划() ...

  7. js基础知识总结(2016.11.1)

    js基础知识点总结 如何在一个网站或者一个页面,去书写你的js代码:1.js的分层(功能):jquery(tool) 组件(ui) 应用(app),mvc(backboneJs)2.js的规划():避 ...

  8. Node.js基础知识

    Node.js入门   Node.js     Node.js是一套用来编写高性能网络服务器的JavaScript工具包,一系列的变化由此开始.比较独特的是,Node.js会假设在POSIX环境下运行 ...

  9. js基础查漏补缺(更新)

    js基础查漏补缺: 1. NaN != NaN: 复制数组可以用slice: 数组的sort.reverse等方法都会改变自身: Map是一组键值对的结构,Set是key的集合: Array.Map. ...

随机推荐

  1. Android解析WindowManager(二)Window的属性

    前言 在上一篇文章我们学习了WindowManager体系,了解了Window和WindowManager之间的关系,这一篇我们接着来学习Window的属性. 1.概述 上一篇文章中我们讲过了Wind ...

  2. Mysql的命令

    学习mysql命令必须先安装哦:安装教程 1.doc连接Mysql 格式: mysql -h主机地址 -u用户名 -p用户密码 1.连接到本机上的MYSQL.首先打开DOS窗口,然后进入目录mysql ...

  3. Windows10禁用update

    1.运行 gpedit.msc 或搜索 组策略 ,点 计算机配置—管理模板—Windows组件—Windows更新—配置自动更新 ,设置“已禁用”.2.点 控制面板—系统和安全—管理工具 ,点 服务 ...

  4. CentOS随笔 - 2.CentOS7安装ftp支持(vsftpd)

    前言 转帖请注明出处: http://www.cnblogs.com/Troy-Lv5/ 在前一篇文章中介绍了在虚拟机中安装CentOS7, 接下来就要进行配置了, 第一个就是安装ftp支持. 要不然 ...

  5. 在ASP.NET MVC中,使用Bundle来打包压缩js和css(转)

    转自:http://www.cnblogs.com/xwgli/p/3296809.html 在ASP.NET MVC4中(在WebForm中应该也有),有一个叫做Bundle的东西,它用来将js和c ...

  6. ZT 布列瑟农

    布列瑟农 编辑 <布列瑟农>(Bressanone)是加拿大环保音乐家马修·连恩(Matthew Lien)创作并演唱的一首经典歌曲,收录在他1995年的专辑<狼>(Bleed ...

  7. ZT自老罗的博客 Android系统的智能指针(轻量级指针、强指针和弱指针)的实现原理分析

    Android系统的智能指针(轻量级指针.强指针和弱指针)的实现原理分析 分类: Android 2011-09-23 00:59 31568人阅读 评论(42) 收藏 举报 androidclass ...

  8. 阿里云 CentOS 镜像和 EPEL 源

    配置阿里云网络yum源 阿里云镜像源地址http://mirrors.aliyun.com/ 1.备份 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.re ...

  9. fzu_oop_east 第二次作业

    这次有四题: 题目1:(这题本身没难度,就是听说格式比较坑,好像) 代码: #include<iostream> #include<cstdio> using namespac ...

  10. [原]外网访问用azure虚拟机搭建的网站

    1.Ubuntu+Apache+PHP的环境搭建(此处省去了mysql的步骤) 在azure上搭建上述的开发环境和在本地PC搭建是一样的步骤,具体介绍请参看这里. 2.从外网访问 注意,这一步的前提是 ...