JavaScript中的闭包与匿名函数

知识内容:

1.预备知识 - 函数表达式

2.匿名函数

3.闭包

一、函数表达式

1.定义函数的两种方式

函数声明:

1 function func(arg0, arg1, arg2){
2     // 函数体
3 }

函数表达式:

1 var func = function (arg0, arg1, arg2){
2     // 函数体
3 }

2.注意事项

函数表达式使用前必须赋值!像下面的代码是错误的:

1 say()
2 var say = function(){
3     console.log("123")
4 }

函数表达式可以作为一个普通变量在分支中根据条件来赋值:

 1 // 下面的代码将根据不同的condition 将不同的函数表达式赋给sayHi
 2 var sayHi
 3 var condition = true  // Hi!
 4 // var condition = false  // Hello!
 5
 6 if(condition){
 7     sayHi = function(){
 8         console.log("Hi!")
 9     }
10 }
11 else{
12     sayHi = function(){
13          console.log("Hello!")
14     }
15 }
16
17 sayHi()

上述代码不同的运行效果:

能创建函数赋值给变量,当然也可以把函数作为其他函数的返回值返回

二、匿名函数

1.什么是匿名函数

匿名函数:一般用到匿名函数的时候都是立即执行的。通常叫做自执行匿名函数或者自调用匿名函数。常用来构建沙箱模式,作用是开辟封闭的变量作用域环境,在多人联合工作中,合并js代码后,不会出现相同变量互相冲突的问题

2.匿名函数的详细写法

匿名函数的详细写法有以下两种,推荐使用第一种:

1 (function(){
2   console.log("我是匿名方式1");
3 })();  // 我是匿名方式1
4
5 (function(){
6   console.log("我是匿名方式2");
7 }());  // 我是匿名方式2
8
9 console.log((function(){}).name);  // name为空

注:实际上,立即执行的匿名函数并不是函数,因为已经执行过了,所以它是一个结果,这个结果是对当前这个匿名函数执行结果的一个引用(函数执行默认return undefined)。这个结果可以是一个字符串、数字或者null/false/true,也可以是对象、数组或者一个函数(对象和数组都可以包含函数),当返回的结果包含函数时,这个立即执行的匿名函数所返回的结果就是典型的闭包了

三、闭包

1.什么是闭包

什么是闭包:闭包指有权访问另一个函数作用域中的变量的函数,官方对闭包的解释是:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分

  • 作为一个函数变量的一个引用,当函数返回时,其处于激活状态
  • 一个闭包就是当一个函数返回时,一个没有释放资源的栈区

简单说Javascript允许使用内部函数---即函数定义和函数表达式位于另一个函数的函数体内。而且这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包

2.闭包的写法


1

2

3

4

5

6

7

8

9

10

function a(){

  var n = 0;

  function inc() {

    n++;

    console.log(n);

  }

  inc();

  inc();

}

a(); //控制台输出1,再输出2

简单吧。再来看一段代码:


1

2

3

4

5

6

7

8

9

10

function a(){

  var n = 0;

  this.inc = function () {

    n++;

    console.log(n);

  };

}

var c = new a();

c.inc();  //控制台输出1

c.inc();  //控制台输出2

简单吧。

什么是闭包?这就是闭包!

有权访问另一个函数作用域内变量的函数都是闭包。这里 inc 函数访问了构造函数 a 里面的变量 n,所以形成了一个闭包。

再来看一段代码:


1

2

3

4

5

6

7

8

9

10

11

function a(){

  var n = 0;

  function inc(){

    n++;

    console.log(n);

  }

  return inc;

}

var c = a();

c();  //控制台输出1

c();  //控制台输出2

看看是怎么执行的:

var c = couter(),这一句 couter()返回的是函数 inc,那这句等同于 var c = inc;

c(),这一句等同于 inc();  注意,函数名只是一个标识(指向函数的指针),而()才是执行函数。

后面三句翻译过来就是:  var c = inc;  inc();  inc();,跟第一段代码有区别吗? 没有

这样写的原因:
我们知道,js的每个函数都是一个个小黑屋,它可以获取外界信息,但是外界却无法直接看到里面的内容。将变量 n 放进小黑屋里,除了 inc 函数之外,没有其他办法能接触到变量 n,而且在函数 a 外定义同名的变量 n 也是互不影响的,这就是所谓的增强“封装性”。

而之所以要用 return 返回函数标识 inc,是因为在 a 函数外部无法直接调用 inc 函数,所以 return inc 与外部联系起来,代码 2 中的 this 也是将 inc 与外部联系起来而已

3.闭包的作用

  • 实现匿名自执行函数
  • 实现封装
  • 实现类和继承

(1)闭包实现匿名自执行函数见下面的匿名函数中的详细介绍

(2)闭包实现封装

JavaScript中没有私有成员的概念,所有对象属性都是公有的,不过却有一个私有变量的概念。简单说任何定义在函数中的变量都可以认为是私有变量,因为不能在函数的外部访问这些变量

私有变量包括:

  • 函数的参数
  • 函数的局部变量
  • 在函数内部定义的其他函数

另外利用闭包原理,我们可以创建用于访问私有变量的公有方法:在函数内部创建一个闭包,闭包通过作用域链访问这些变量,从而实现外部无法直接访问内部变量只能通过某些方法来访问,这样就实现了封装

 1 var person = function(){
 2     // 变量作用域为函数内部,外部无法访问
 3     var name = "default";
 4
 5     return {
 6        getName : function(){
 7            return name;
 8        },
 9        setName : function(newName){
10            name = newName;
11        }
12     }
13 }();
14
15 print(person.name);  // 直接访问,结果为undefined
16 print(person.getName());
17 person.setName("wyb");
18 print(person.getName());
19    

(3)闭包实现类和继承

 1 function Person(){
 2     var name = "default";
 3
 4     return {
 5        getName : function(){
 6            return name;
 7        },
 8        setName : function(newName){
 9            name = newName;
10        }
11     }
12     };
13
14     var p = new Person();
15     p.setName("Tom");
16     alert(p.getName());
17
18     var s= function(){};
19     // 继承自Person
20     s.prototype = new Person();
21     // 添加私有方法
22     s.prototype.Say = function(){
23         alert("Hello,my name is s");
24     };
25     var j = new s();
26     j.setName("s");
27     j.Say();
28     alert(j.getName());

4.闭包注意事项

(1)闭包与变量

闭包只能取得包含函数中任何变量的最后一个值,闭包所保存的是整个变量对象,而不是某个特殊的变量

 1 function createFunctions(){
 2     var result = new Array()
 3
 4     for (var i=0; i < 10; i++){
 5         result[i] = function(){
 6             return i;
 7         }
 8     }
 9
10     return result
11 }
12
13 var foo = createFunction()

结果foo中并不是预料中的那样,而是10,这是为什么呢?仅仅声明某一个函数,引擎并不会对函数内部的任何变量进行查找或赋值操作。只会对函数内部的语法错误进行检查(如果往内部函数加上非法语句,那么不用调用也会报错),也就是说在返回result之前

result并未执行,返回之后再执行时匿名函数中的所有i引用的都是同一个变量(最后一个值10),故每个函数内部i的值为10

详细说明看这一篇文章:https://www.cnblogs.com/kindofblue/p/4907847.html

通过创建另一个匿名函数强制让闭包的行为符合预期:

 1 function createFunctions(){
 2     var result = new Array()
 3
 4     for (var i=0; i < 10; i++){
 5         result[i] = function(num){
 6             return function(){
 7                  return num
 8             }
 9         }(i)
10     }
11
12     return result
13 }  

(2)闭包中的this对象

关于this对象:

  • this对象是在运行时基于函数的执行环境绑定的
  • 在全局函数中this等价于window,当函数被作为某个对象的方法调用时this等价于那个对象
  • 在闭包中使用this对象可能会导致一些问题

 1 var name = "The Window";
 2 var object = {
 3     name: "My object",
 4     getNameFunc: function() {
 5         return function() {
 6             return this.name;
 7         };
 8     }
 9 }
10 alert(object.getNameFunc()()); // "The Window"

为什么最后的结果是"The Window"而不是object里面的name"My object"呢?

首先,要理解函数作为函数调用和函数作为方法调用,我们把最后的一句拆成两个步骤执行:

var first = object.getNameFunc();
var second = first();

其中第一步,获得的first为返回的匿名函数,此时的getNameFunc()作为object的方法调用,如果在getNameFunc()中使用this,此时的this指向的是object对象。

第二步,调用first函数,可以很清楚的发现,此时调用first函数,first函数没有在对象中调用,因此是作为函数调用的,是在全局作用域下,因此first函数中的this指向的是window

再看下面这句话:

为什么匿名函数没有取得其包含作用域(外部作用域)的this对象呢?

每个函数被调用时,其活动对象都会自动取得两个特殊变量:this和arguments。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量。  《Javascript高级程序设计》

那么,如何获得外部作用域中的this呢?可以把外部作用域中的this保存在闭包可以访问到的变量里。如下:

 1 var name = "The Window";
 2 var object = {
 3     name: "My object",
 4     getNameFunc: function() {
 5         var that = this;   // 将getNameFunc()的this保存在that变量中
 6         var age = 15;
 7         return function() {
 8             return that.name;
 9         };
10     }
11 }
12 alert(object.getNameFunc()());   // "My object"

(3)模仿块级作用域

在JavaScript中没有块级作用域,JavaScript从来不会告诉你是否多次同时声明同一个变量,遇到这种情况它只会对后续的声明视而不见,不过我们可以使用匿名函数和闭包来实现块级作用域

用作块级作用域的匿名函数如下:

1 (function(){
2    // 这里是块级作用域
3 }) ();

上述匿名函数也等价于这种形式:

1 var someFunction = function(){
2     // 这里是块级作用域
3 }
4 someFunction()

关于匿名函数及模仿块级作用域的注意事项:

  • 在匿名函数中定义的任何变量都会在执行结束时被销毁
  • 在私有作用域(匿名函数)中可以访问外层函数的变量是因为这个匿名函数是一个闭包,它能够访问包含作用域中的所有变量

关于以上两点,示例代码如下:

1 function func(count){
2     (function (){
3         for(var i=0; i < count; i++){
4             console.log(i)
5         }
6     })();
7
8     console.log(i)  // 导致一个错误!
9 }

原文地址:https://www.cnblogs.com/wyb666/p/9314141.html

时间: 2024-10-13 00:19:03

JavaScript中的闭包与匿名函数的相关文章

PHP中的闭包和匿名函数

PHP中的闭包和匿名函数 闭包是指在创建时封装周围状态的函数.即使闭包所在的环境不存在了,闭包中封装的状态依然存在. 匿名函数就是没有名称的函数.匿名函数可以赋值给变量,还能像其他任何PHP对象那样传递.不过匿名函数仍是函数,因此可以调用,还可以传入参数.匿名函数特别适合作为函数或方法的回调. 注意:理论上讲,闭包和匿名函数是不同的概念.不过,PHP将其视作相同的概念.所以,我们提到闭包时,指的也是匿名函数,反之亦然. PHP闭包和匿名函数使用的句法与普通函数相同,但闭包和匿名函数其实是伪装成函

浅析PHP中的闭包和匿名函数

PHP闭包和匿名函数使用的句法与普通函数相同,但闭包和匿名函数其实是伪装成函数的对象(Closure类的实例) .下面给大家介绍PHP中的闭包和匿名函数知识,需要的朋友参考下吧 闭包是指在创建时封装周围状态的函数.即使闭包所在的环境不存在了,闭包中封装的状态依然存在. 匿名函数就是没有名称的函数.匿名函数可以赋值给变量,还能像其他任何PHP对象那样传递.不过匿名函数仍是函数,因此可以调用,还可以传入参数.匿名函数特别适合作为函数或方法的回调. 注意:理论上讲,闭包和匿名函数是不同的概念.不过,P

一篇文章把你带入到JavaScript中的闭包与高级函数

在JavaScript中,函数是一等公民.JavaScript是一门面向对象的编程语言,但是同时也有很多函数式编程的特性,如Lambda表达式,闭包,高阶函数等,函数式编程时一种编程范式. function dada() { var a = 1; var b = function() { console.log(a); } return b // b 就是一个闭包函数,因为它能访问dada函数的作用域 } JavaScript的函数也是对象,可以有属性,可以赋值给一个变量,可以放在数组里作为元素

Javascript中的自执行匿名函数(个人理解也叫立即执行的匿名函数)的理解

格式: (function(){ //代码 })(); 包围函数(function(){})的第一对括号向脚本返回未命名的函数,随后一对空括号立即执行返回的未命名函数,括号内为匿名函数的参数. (function(arg){ alert(arg+100); })(20); // 这个例子返回120. 回来看看jquery的插件编写 (function($) { // Code goes here })(jQuery); 这样代码等同于 var a=functon($) {//code }; a(

avascript中的自执行匿名函数

Javascript中的自执行匿名函数 格式: (function(){ //代码 })(); 解释:这是相当优雅的代码(如果你首次看见可能会一头雾水:)),包围函数(function(){})的第一对括号向脚本返回未命名的函数,随后一对空括号立即执行返回的未命名函数,括号内为匿名函数的参数. 来个带参数的例子: (function(arg){ alert(arg+100); })(20); // 这个例子返回120. 重要用途:可以用它创建命名空间,只要把自己所有的代码都写在这个特殊的函数包装

javascript中的闭包、模仿块级作用域和私有变量

闭包是指有权访问另一个函数作用域中的变量的函数.创建闭包的常见方式为:在一个函数内部创建另一个函数. "当某个函数被调用时,会创建一个执行环境(execution context)及相应的作用域链.然后,使用arguments和其他命名参数的值来初始化函数的活动对象(activation object).但在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象出于第三位.....直至作用域链终点的全局执行环境." function creawteCompariso

Javascript中的闭包(转载)

前面的话: 闭包,是 javascript 中重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,你很难从定义去理解它.下面是作者从作用域链慢慢讲到闭包以及在后面提到了一些闭包的高级用法.下面大家一起来学习Javascript中的闭包. 谈一谈JavaScript作用域链 当执行一段JavaScript代码(全局代码或函数)时,JavaScript引擎会创建为其创建一个作用域又称为执行上下文(Execution Context),在页面加载后会

Javascript 中的闭包和引用

Javascript 中一个最重要的特性就是闭包的使用.因为闭包的使用,当前作用域总可以访问外部的作用域.因为Javascript 没有块级作用域,只有函数作用域,所以闭包的使用与函数是紧密相关的. 模拟私有变量 function Counter(start) { var count = start; return { increment: function() { count++; }, get: function() { return count; } } } var foo = Count

php闭包与匿名函数

不知不觉发现PHP已经出到了5.5版本,而自己一直在用PHP5.2,让我看起来像深山出来的小伙子一样,又土又落后.在我习惯在javascript中使用闭包之后,忽然间对PHP的闭包打起了兴趣. 于是乎在网上下了个WAMP集成开发环境,是PHP5.3版本的(PHP5.3开始引入了闭包的特性),不得不说WAMP安装使用真的很方便.简单配置了一下,开始动手. 匿名函数 提到闭包就不得不想起匿名函数,也叫闭包函数(closures),貌似PHP闭包实现主要就是靠它.声明一个匿名函数是这样: 1 $fun