闭包(Closures)
浅析 JavaScript 中的闭包(Closures)
一、前言
对于 JavaScript 来说,闭包是一个非常强大的特征。但对于刚开始接触的初学者来说它又似乎是特别高深的。今天我们一起来揭开闭包的神秘面纱。闭包这一块也有很多的文章介绍过了,今天我就浅谈一下自己对闭包的的一些理解,希望能提供一点鄙陋的见解帮助到正在学习的朋友。该文章中能使用口语化的我将尽量使用口语化的叙述方式,希望能让读者更好理解,毕竟文章写出来宗旨就是要让人读懂。文章难免有不足之处还希望帮忙指出。
二、Javascript 的作用域链
在了解闭包之前,我们先来看看几个准备知识。
变量的作用域
首先,什么是作用域?域,区域。简单来理解就是一个变量能被访问的范围(区域)。换言之就是这个变量能起作用的区域。按这个标准来划分我们将变量分为
全局变量
和局部变量
两种以定义的方式来区分有以下特点:
定义在函数内部的变量是局部变量,定义在函数外部的变量是全局变量。(这个并不只是 Javascript 语言的特点)局部变量在函数内部能被访问,在函数外部不能被直接访问,所以局部变量就是从定义它的地方开始到函数结束的位置结束。当然这里有个细节--
变量声明提升
。等下我们用一小段代码提一下变量声明提升是什么。我们先来看看局部变量和全局变量的代码var a = 0; function testFunc(){
var b = 1;
console.log('-------------函数内输出-------------');
console.log(a);//0
console.log(b);//1
} //调用函数
testFunc(); console.log('-------------函数外输出-------------');
console.log(a);//0
console.log(b);//Uncaught ReferenceError: b is not defined执行以上代码结果如下图所示
在代码的最后一行抛出了 b 未定义的异常.也就是说我们在函数外部访问不到在函数内部定义的局部变量。但是第六行代码的正常输出,可见在函数内部我们是可以访问到在函数外部定义的全局变量 a
变量声明提升
相信如果学过 C 语言的话,应该会很熟悉一句话 "先声明后使用"。就是说一个变量或者函数在使用它之前必须是要先找得到这个变量或函数的声明的。例如:
//C 语言正确写法
int a = 0;
printf(a); //错误写法,下面代码没办法通过标准编译(直接报异常)
printf(a);
int a = 0;我们再来看看 Javascript 代码
var a = 0;
console.log(a);//输出结果 0上面这种普通写法我们不探讨,重点看下面的这段代码
console.log(a);//输出结果 undefined
var a = "hello";
console.log(a);//输出结果 hello运行结果如下
上面这个例子就恰好说明了变量声明提升的特点,我们在没有声明变量 a 之前就直接访问变量a 输出结果为 undefined 而并不是直接报异常。所以最直观的感觉是变量的声明被提升到使用之前了。实质上代码如下:
var a;//声明被提升到这里
console.log(a);//输出结果 undefined
a = "hello";
console.log(a);//输出结果 hello小结一下
- 函数内部定义的变量是局部变量,函数外部定义的变量是全局变量。
- 局部变量不能被外界直接访问,全局变量可以在函数内被访问。
- 变量声明提升
嵌套函数的作用域特点
搞清楚上面的小结部分我们缕一缕思路继续探讨另一个话题,javascript 中的嵌套函数,我们先上一段代码:
function A(param){
var vara = 1;
function B(){
var varb = 2;
console.log("----Function B----------")
console.log(vara);//函数B中访问A函数中定义的变量
console.log(param);//A函数中传进来的变量
console.log(varb);//访问自身函数内定义的变量
}
B();
console.log("----Function A----------")
console.log(vara);//访问自身函数内定义的变量
console.log(param);//A函数中传进来的变量
console.log(varb);//访问B函数中定义的变量--异常
}
A("hello");运行结果如下:
由此可见嵌套函数(B)可以继承容器函数(A)的参数和变量,但是嵌套函数(B)中的变量对于他的容器函数来说却是B私有的,也就是说 A 无法访问 B 中定义的变量。换句话说,B 函数形成了一个相对独立的环境(空间)使得它自身的变量只能由它自己来访问,但是 A 函数里的变量 B 也可以访问,这里嵌套函数 B 就形成了一个闭包。有一句话很适合 B 来说 “你的就是我的,我的还是我的”
从语法上看是函数 A 包含着函数 B,但是从作用域上来看是函数 B 的作用域包含着函数 A 的作用域,关系如下图所示:
假设:函数 B 下面又包含了函数 C。此时函数 C 为函数 B 的嵌套函数,函数 B 为函数 C 的容器函数。对于C来说也具有刚刚讲过的 “你的就是我的,我的还是我的” 的特点。以此类推层层嵌套的话就形成了一条链条, 作用域按此规律也形成了 Javascript 中的作用域链。
三、闭包的特点
我们先来总结上面提到的两点
- 嵌套在容器函数(A)内部的嵌套函数(B)只能在容器函数(A)内被访问
- 嵌套函数(B)继承了容器函数(A)的变量,但是 B 函数中的变量只有它自己能访问,也就是嵌套函数(B)的作用域包含容器函数(A)的作用域。
闭包之保存变量
我们还是先上一段代码

function A(a){
function B(b){
return a + b;
}
return B;
}
var C = A(1);
var result = C(2);
console.log(result);//输出结果 3

函数 B 形成了一个闭包,A 函数调用之后返回函数 B 的引用。执行 C 之后发现结果等于3,这也就说明了我们调用 A 的时候 传进去的参数 1 没有被销毁,而是被保存起来了,这就是闭包保存变量的特点。
有保存就有销毁那我们被闭包保存的变量在什么时候销毁?答案是当 B 没有再被引用的时候,就会被销毁.
闭包的注意点--命名冲突
我们还是先上一段代码

function A(){
var num = 6;//外部的名为num 的变量
function B(num){
return num;//当做参数传进来的num 变量,命名冲突发生在这
}
return B;
}
var result = A()(10);
console.log(result);//输出结果10

上述代码的执行结果
通过上面的代码我们能看到有一个容器函数内的名为 num 的变量以及一个嵌套函数内同样名为 num 的变量。这样的执行代码结果以嵌套函数内的变量优先。可能这里说成就近原则更容易记得住。这个就是闭包在实际应用中应该注意的一点。
四、闭包在开发中的应用。
关于闭包在开发中的使用,最多的体现应该还是在 Javascript 插件的开发上面。使用闭包可以避免变量污染。也就是说你在闭包中使用的变量名称不会影响到其他地方同样名称,换个角度来讲,我将我嵌套函数内部的变量给保护起来了,外部没办法随便修改我内部定义的变了。也就是虽然名字一样但是你是你我是我。代码体现如下:

function A(){
function B(num){
var c = 10;//内部变量 c
return num + c;
}
return B;
} var c = 20;//外部变量c
var result = A()(c);
console.log(c);//20
console.log(result)//30

以上特点应用在插件开发中就可以很好的保护了插件本身,避免了外界的串改,保证了插件的稳定。
简单的插件
初步代码

//编写插件代码
var plugin = (function(){
function SayHi(str = '你好啊!'){
console.log(str);
}
return SayHi;
})(); //使用插件
plugin('hello');
plugin();

上面代码闭包部分我就不在累述了,我们来看看新出现的一种语法--自调用匿名函数:
(function{
//code
})();
实际作用是创建了一个匿名函数,并在创建后立即执行一次。作用等价于下面的代码,唯一的区别就是下面的函数不是匿名的。

//创建
var func = function(){
//code
}
//调用
func();

当然,我们编写插件不可能只提供一个API给外部使用,如何返回多个API,我们这里使用字面量形式返回。改进之后的代码如下

//编写插件代码
var plugin = (function(){
var _sayhi = function(str = '你好啊!'){
console.log(str);
}
var _sayhello = function(){
console.log("这个API能做很牛逼的事情");
}
return {
SayHi : _sayhi,
SayHello : _sayhello
}
})(); //通过插件提供的API使用插件
plugin.SayHi('hello');
plugin.SayHello();

执行结果
五、后语
今天对于闭包的看法暂时先写到这了,秉承着学以致用的原则,下两篇文章我将介绍 javascript 插件的几种开发形式,以及实践--开发一个原生的 Javascript 插件。
闭包(Closures)的更多相关文章
- Swift学习:闭包(Closures)
/* 闭包(Closures)* 闭包是自包含的功能代码块,可以在代码中使用或者用来作为参数传值.* 在Swift中的闭包与C.OC中的blocks和其它编程语言(如Python)中的lambdas类 ...
- Swift学习之十四:闭包(Closures)
* 闭包(Closures) * 闭包是自包含的功能代码块,可以在代码中使用或者用来作为参数传值. * 在Swift中的闭包与C.OC中的blocks和其它编程语言(如Python)中的lambdas ...
- The Swift Programming Language-官方教程精译Swift(8)闭包 -- Closures
闭包是功能性自包含模块,可以在代码中被传递和使用. Swift 中的闭包与 C 和 Objective-C中的 blocks 以及其他一些编程语言中的 lambdas 比较相似. 闭包可以捕获和存储其 ...
- Swift:闭包(Closures)
一. 基本概念 闭包(Closures)是自包括的功能代码块,能够在代码中使用或者用来作为參数传值. 在Swift中的闭包与C.OC中的blocks和其他编程语言(如C#)中的lambda, java ...
- 闭包Closures
所谓闭包,可以理解为一个可以用于函数,参数,返回值处的代码块 import Foundation func isGood(a:Int,b:Int)->Bool{ return a>b; } ...
- 浅析 JavaScript 中的闭包(Closures)
a { text-decoration: none; color: #4094c7 } h4,h5 { margin: 0; font-weight: 700; color: inherit; lin ...
- [PHP] PHP闭包(closures)
1.闭包函数也叫匿名函数,一个没有指定名称的函数,一般会用在回调部分 2.闭包作为回调的基本使用, echo preg_replace_callback('~-([a-z])~', function ...
- [译]Javascript中的闭包(closures)
本文翻译youtube上的up主kudvenkat的javascript tutorial播放单 源地址在此: https://www.youtube.com/watch?v=PMsVM7rjupU& ...
- atitit.闭包的概念与理解attilax总结v2 qb18.doc
atitit.闭包的概念与理解attilax总结v2 qb18.doc 1.1. 闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数.1 2. #---- ...
随机推荐
- 转:Nginx+ffmpeg的HLS开源服务器搭建配置及开发详解
转:http://itindex.net/detail/51186-nginx-ffmpeg-hls 本文概述: 至目前为止,HLS 是移动平台上非常重要并十分流行的流媒体传输协议.做移动平台的流媒体 ...
- IIS 发布网站到外网
前段时间做了一个项目在局域网中测试后要发布到外网上,一时间不知怎么搞,以为直接在IIS中修改发布时的IP就可以了,但是不可行,经过摸索终于成功发布到外网,下面是具体步骤. 前期准备:公网IP,掩码,网 ...
- unity3d首次倒入工程文件出错Opening file Library/FailedAssetImports.txt failed解决方法
打开unity3d,首次倒入工程到unity编辑器,但是频繁弹出“Opening file Library/FailedAssetImports.txt failed”的错误对话框,很麻烦. 解决方法 ...
- UML——类和对象
- MYSQL 排行类的相关SQL写法,仅供参考
SELECT * FROM () )) b
- javascript中常用的一些功能及正则表达式的用法
一.取得地址栏后的参数 /** * 假设地址栏url为:login.do?username = "这里中文的话会是特殊字符组成的" */ //定义一个取得参数值的函数 functi ...
- 【oracle】oracle函数-数值函数
一.数值函数 1. mod(m,n) 求余函数 注意:若m或者n为null,则返回null.若n为0,则返回m的值 eg:
- c#中设置像数量,价格,金额等的textbox的限制条件,用户只能输入数字或小数
#region 设置数量等textbox控件样式及限制条件(具体调用的方法就是重写或直接调用ShieldNumberTextBoxOtherKeys函数) /// <summary> // ...
- OC12_自动释放池
// // Dog.h // OC12_自动释放池 // // Created by zhangxueming on 15/6/18. // Copyright (c) 2015年 zhangxuem ...
- mongoDB知识总结
官方说明文档:https://docs.mongodb.com/manual/mongo/ 1 NoSQL 简介 NoSQL,全称是”Not Only Sql”,指的是非关系型的数据库(相对于关系型数 ...