js基础(闭包)
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 中闭包的理解,如果有误,欢迎指正。最后引用一段知乎问题下关于闭包概念的一个回答。
什么是闭包?
简单来说,闭包是指可以访问另一个函数作用域变量的函数,一般是定义在外层函数中的内层函数。
为什么需要闭包?
局部变量无法共享和长久的保存,而全局变量可能造成变量污染,所以我们希望有一种机制既可以长久的保存变量又不会造成全局污染。
特点
- 占用更多内存
- 不容易被释放
何时使用?
变量既想反复使用,又想避免全局污染
如何使用?
- 定义外层函数,封装被保护的局部变量。
- 定义内层函数,执行对外部函数变量的操作。
- 外层函数返回内层函数的对象,并且外层函数被调用,结果保存在一个全局的变量中。
以下为常用发送短信的闭包实现:
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基础(闭包)的更多相关文章
- js基础闭包练习题
题目描述 实现函数 makeClosures,调用之后满足如下条件:1.返回一个函数数组 result,长度与 arr 相同2.运行 result 中第 i 个函数,即 result[i](),结果与 ...
- JavaScript基础—闭包,事件
Js基础-闭包,事件 1:js中的闭包 概念:在一个函数内部又定义了一个函数,内部函数能访问到外部函数作用域范围内的变量,这时这个内部函数就叫做闭包,无论这个内部函数在哪里被调用都能访问到外部函数作用 ...
- JS基础学习——闭包
JS基础学习--闭包 什么是闭包 闭包的定义如下,它的意思是闭包使得函数可以记住和访问它的词法范围,即使函数是在它声明的词法范围外执行.更简单来讲,函数为了自己能够正确执行,它对自己的词法范围产生闭包 ...
- JS基础篇之作用域、执行上下文、this、闭包
前言:JS 的作用域.执行上下文.this.闭包是老生常谈的话题,也是新手比较懵懂的知识点.当然即便你作为老手,也未必真的能理解透彻这些概念. 一.作用域和执行上下文 作用域: js中的作用域是词法作 ...
- js 基础
js基础知识点总结 如何在一个网站或者一个页面,去书写你的js代码:1.js的分层(功能):jquery(tool) 组件(ui) 应用(app),mvc(backboneJs)2.js的规划():避 ...
- JS基础知识总结
js基础知识点总结 如何在一个网站或者一个页面,去书写你的js代码:1.js的分层(功能):jquery(tool) 组件(ui) 应用(app),mvc(backboneJs)2.js的规划() ...
- js基础知识总结(2016.11.1)
js基础知识点总结 如何在一个网站或者一个页面,去书写你的js代码:1.js的分层(功能):jquery(tool) 组件(ui) 应用(app),mvc(backboneJs)2.js的规划():避 ...
- Node.js基础知识
Node.js入门 Node.js Node.js是一套用来编写高性能网络服务器的JavaScript工具包,一系列的变化由此开始.比较独特的是,Node.js会假设在POSIX环境下运行 ...
- js基础查漏补缺(更新)
js基础查漏补缺: 1. NaN != NaN: 复制数组可以用slice: 数组的sort.reverse等方法都会改变自身: Map是一组键值对的结构,Set是key的集合: Array.Map. ...
随机推荐
- Java 开源博客 Solo 1.5.0 发布 - 新皮肤
Solo 1.5.0 正式发布了!这个版本主要是加入了 一款新皮肤 next,感谢一直以来关注和支持我们的朋友! 只需一个命令即可启动(不需要安装数据库.部署容器):也可以通过 war 方式部署容器, ...
- 从零开始安装、编译、部署 Docker
简介 主要介绍如何从基础系统debian部署docker关于docker基础知识在 相关资料 里有链接 安装docker 1.使用root用户身份添加apt源添加public key使docker的安 ...
- CentOS6.5 SSH无法连接
1.确认连接交机机端口的正常,如查更换多几个端口试 2.确认网络不存在问题. 3.把selinux iptables服务全部关闭 4.查看本机是否侦听22端口和是否开启ssh服务 命令:netstat ...
- 个人总结-7- 实现图片在MySQL数据库中的存储,取出以及显示在jsp页面上
昨天主要是进行对数据库的内容提取出来并进行动态显示,这个只需要设置一个servlet从数据库中获取数据即可,只是图片比较特殊,不能显示. 今天准备继续找方法来实现图片得录入和显示到jsp中,准备从网上 ...
- Anaconda管理多版本的python环境
通过Conda的环境管理功能,我们能同时安装多个不同版本的Python,并能根据需要自由切换.下面我将给大家分享一下,新增Python版本,切换,再切回主版本的详细过程. 方法/步骤 1 首先确保 ...
- [翻译] JTSlideShadowAnimation
JTSlideShadowAnimation 效果图: JTSlideShadowAnimation allow you to reproduce the famous "slide to ...
- Linux入门-4 Linux下获取帮助
help MAN INFO doc help <command> -h或<command> --help whatis <cmd> MAN man <comm ...
- 第三课 java编程入门
java特点: 1.面对象性 2.可移植性/跨平台性 java组成: jdk(java工具开发工具包) / \ \ jre 指令集合 api和常用 ...
- Web前端——jQuery----细节
jQuery终极思想:创建jQuery对象(当发现相同的选择器在你的代码里出现多次时,请用变量把它储存起来.一切面向对象) 认识jQuery 1.jQuery库可以做什么? HTML元素选取 HTML ...
- 沉淀再出发:dubbo的基本原理和应用实例
沉淀再出发:dubbo的基本原理和应用实例 一.前言 阿里开发的dubbo作为服务治理的工具,在分布式开发中有着重要的意义,这里我们主要专注于dubbo的架构,基本原理以及在Windows下面开发出来 ...