Javascript中的闭包(转载)

前面的话:

  闭包,是 javascript 中重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,你很难从定义去理解它。下面是作者从作用域链慢慢讲到闭包以及在后面提到了一些闭包的高级用法。下面大家一起来学习Javascript中的闭包。

谈一谈JavaScript作用域链

  当执行一段JavaScript代码(全局代码或函数)时,JavaScript引擎会创建为其创建一个作用域又称为执行上下文(Execution Context),在页面加载后会首先创建一个全局的作用域,然后每执行一个函数,会建立一个对应的作用域,从而形成了一条作用域链。每个作用域都有一条对应的作用域链,链头是全局作用域,链尾是当前函数作用域。

  作用域链的作用是用于解析标识符,当函数被创建时(不是执行),会将this、arguments、命名参数和该函数中的所有局部变量添加到该当前作用域中,当JavaScript需要查找变量X的时候(这个过程称为变量解析),它首先会从作用域链中的链尾也就是当前作用域进行查找是否有X属性,如果没有找到就顺着作用域链继续查找,直到查找到链头,也就是全局作用域链,仍未找到该变量的话,就认为这段代码的作用域链上不存在x变量,并抛出一个引用错误(ReferenceError)的异常。

看下面的例子:

//定义全局变量color,对于全局都适用,即在任何地方都可以使用全局变量color
var color = "red";

function changeColor(){
    //在changeColor()函数内部定义局部变量anotherColor,只在函数changeColor()里面有效
    var anotherColor = "blue";

    function swapColor(){
        //在swapColor()函数内部定义局部变量tempColor,只在函数swapColor()里面有效
        var tempColor = anotherColor;
        anotherColor = color;
        color = tempColor;

        //这里可以访问color、anotherColor和tempColor
        console.log(color);                //blue
        console.log(anotherColor);        //red
        console.log(tempColor);            //blue
    }

    swapColor();
    //这里只能访问color,不能访问anotherColor、tempColor
    console.log(color);                //blue
    console.log(anotherColor);        //anotherColor is not defined
    console.log(tempColor);            //tempColor is not defined
}

changeColor();
//这里只能访问color
console.log(color);                //blue
console.log(anotherColor);        //anotherColor is not defined
console.log(tempColor);            //tempColor is not defined

 还有几个坑需要注意一下:

1、var和函数的提前声明

var color = "red";

function changeColor(){
    var color = "yellow";
    return color;
}

var result = changeColor();
console.log(result);

再如:

function fn(a) {
  console.log(a);
  var a = 2;
  function a() {}
  console.log(a);
}
fn(1);
//输出:function a() {} ,2

2、Javascript中没有块级作用域,但是有词法作用域,比如:

function f1(){var a=1;f2();}
function f2(){return a;}
var result = f1();
console.log(result);
//输出结果:a is not defined

3、在函数内部不用var关键字申明变量,则默认该变量为全局变量,比如:

function add(a,b){
    var sum = a+b;//次世代sum为add函数内部的变量,仅限在函数内部使用,在函数外面不可以使用
    return sum;
}
var result = add(1,2);
console.log(result);    //3
console.log(sum);        //sum is not defined
//不使用var关键字声明变量
function add(a,b){
    sum = a+b;//此时的sum为全局变量,在函数之外也可以调用
    return sum;
}
var result = add(1,2);
console.log(result);    //3
console.log(sum);        //3

补充:

在JavaScript中如果不创建变量,直接去使用,则报错:


1

2

console.log(xxoo);

// 报错:Uncaught ReferenceError: xxoo is not defined

JavaScript中如果创建值而不赋值,则该值为 undefined,如:


1

2

3

var xxoo;

console.log(xxoo);

// 输出:undefined

在函数内如果这么写:


1

2

3

4

5

6

7

function Foo(){

    console.log(xo);

    var xo = ‘seven‘;

}

 

Foo();

// 输出:undefined

上述代码,不报错而是输出 undefined,其原因是:JavaScript的函数在被执行之前,会将其中的变量全部声明,而不赋值。所以,相当于上述实例中,函数在“预编译”时,已经执行了var xo;所以上述代码中输出的是undefined。

注意:我们平时在声明变量时一定要注意!!!还有不要滥用全局变量(在forin循环的时候特别注意)!!!

4、词法作用域是不可逆的,我们可以从下面的例子中看到结果:

// name = undefined
var scope1 = function () {
  // name = undefined
  var scope2 = function () {
    // name = undefined
    var scope3 = function () {
      var name = ‘Todd‘; // locally scoped
    };
  };
};



前面我们了解了作用域的一些基本知识,我们发现有作用域的存在能帮我们省去不少事,但是于此同时,也给我们带来了很多麻烦,比如说我们想在下面的函数A中,调用函数B,我们该怎么办呢?

function A(){
    function B(){
           //
    }
}

思路:我们给函数B设一个返回值,然后在函数A中调用,代码如下:

function A(){
    function B(){
       console.log("Hello foodoir!");
    }
    return B;
}
var c = A();
c();//Hello foodoir!

这样我们就可以得到我们想要的结果。这样,我们基本上到了一个最简单的闭包形式。我们再回过头分析代码:


1

2

3

4

5

(1)定义了一个普通函数A

(2)在A中定义了普通函数B

(3)在A中返回B(确切的讲,在A中返回B的引用)

(4)执行A(),把A的返回结果赋值给变量 c

(5)执行 c()

把这5步操作总结成一句话:函数A的内部函数B被函数A外的一个变量 c 引用。当一个内部函数被其外部函数之外的变量引用时,就形成了一个闭包。

思考:我们还有没有其他的方法?

思路:使用匿名函数

function A(){
    //匿名函数
    var B = function(x,y) {
        return x+y;
    }
    console.log(B(1,2));//3
    return B(1,2);
}
var c = A();
console.log(c);//3

然而,在Javascript高级程序设计中是这样描述闭包的“闭包是指有权访问另一个函数作用域中的变量的函数”,但是我们看匿名函数的例子,很明显,这种方法不可取!
通过这个例子,能让我们更好的理解闭包。

下面我们再来看下面的几种闭包

demo1:

function fn(){
    var b = "foodoir";
    return function(){
        console.log(b);//foodoir
        return b;
    }
}
//console.log(b);//b is not defined
var result = fn();
console.log(result());//foodoir

demo2:

var n;
function f(){
    var b = "foodoir";
    n = function(){
        return b;
    }
}
f();
console.log(n());//foodoir

demo3:

//相关定义与闭包
function f(arg){
    var n = function(){
        return arg;
    };
    arg++;
    return n;
}
var m = f(123);
console.log(m());//124
//注意,当我们返回函数被调用时,arg++已经执行过一次递增操作了,所以m()返回的是更新后的值。

demo4:闭包中的读取与修改

//闭包中的设置与修改
var getValue,setValue;
(function(){
    var n = 0;
    getValue = function(){
        return n;
    };
    setValue = function(x){
        n = x;
    }
})();
//console.log(n);
console.log(getValue());//0
console.log(setValue());//undefined

setValue(123);
console.log(getValue());//123

demo5:用闭包实现迭代效果

//用闭包实现迭代器效果
function test(x){
    //得到一个数组内部指针的函数
    var i=0;
    return function(){
        return x[i++];
    };
}
var next = test(["a","b","c","d"]);
console.log(next());//a
console.log(next());//b
console.log(next());//c
console.log(next());//d

demo6:循环中的闭包

//循环中的闭包
function fn(){
    var a = [];
    for(var i=0;i<3;i++){
        a[i] = function(){
            return i;
        }
    }
    return a;
}
var a = fn();
console.log(a[0]());//3
console.log(a[1]());//3
console.log(a[2]());//3

/*
 * 我们这里创建的三个闭包,结果都指向一个共同的局部变量i。
 * 但是闭包并不会记录它们的值,它们所拥有的只是一个i的连接,因此只能返回i的当前值。
 * 由于循环结束时i的值为3,所以这三个函数都指向了3这一个共同值。
 * */

思考:如何使结果输出分别为0、1、2呢?

思路一:我们可以尝试使用自调用函数

function fn(){
    var a = [];
    for(var i=0;i<3;i++){
        a[i] = (function(x){
            return function(){
                return x;
            }
        })(i);
    }
    return a;
}
var a = fn();
console.log(a[0]());//0
console.log(a[1]());//1
console.log(a[2]());//2

思路二:我们将i值本地化

function fa(){
    function fb(x){
        return function(){
            return x;
        }
    }
    var a = [];
    for(var i=0;i<3;i++){
        a[i] = fb(i)
    }
    return a;
}
console.log(a[0]());//0
console.log(a[1]());//1
console.log(a[2]());//2

------------------------------------------------------分界线-------------------------------------------------------

在这里,我们来对闭包进行更深一步的操作

我们再将demo1的例子进行扩展

代码示例如下:

function funcTest(){
  var tmpNum=100; //私有变量
  //在函数funcTest内
  //定义另外的函数作为funcTest的方法函数
  function innerFuncTest(
  {
       alert(tmpNum);
       //引用外层函数funcTest的临时变量tmpNum
  }

  return innerFuncTest; //返回内部函数
}

//调用函数
var myFuncTest=funcTest();
myFuncTest();//弹出100

到样,我们对闭包的概念和用法有更加熟悉

闭包和this相关

闭包应用举例,模拟类的私有属性,利用闭包的性质,局部变量只有在sayAge方法中才可以访问,而name在外部也访问,从而实现了类的私有属性。

function User(){
    this.name = "foodoir";  //共有属性
    var age = 21;    //私有属性
    this.sayAge=function(){
        console.log("my age is " + age);
    }
}
var user = new User();
console.log(user.name); //"foodoir"
console.log(user.age);  //"undefined"
user.sayAge();   //"my age is 21"

关于闭包更深入的了解
前面在demo6中,我们了解了用自调用方法来实现闭包,下面我们用这种方法来进行更复杂的操作(写一个简单的组件)。

(function(document){
    var viewport;
    var obj = {
        init:function(id){
           viewport = document.querySelector("#"+id);
        },
        addChild:function(child){
            viewport.appendChild(child);
        },
        removeChild:function(child){
            viewport.removeChild(child);
        }
    }
    window.jView = obj;
})(document);

这个组件的作用是:初始化一个容器,然后可以给这个容器添加子容器,也可以移除一个容器。功能很简单,但这里涉及到了另外一个概念:立即执行函数。 简单了解一下就行。主要是要理解这种写法是怎么实现闭包功能的。

闭包并不是万能的,它也有它的缺点

  1、闭包会使得函数中的变量都保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页性能问题。另外在IE下有可能引发内存泄漏 (内存泄漏指当你的页面跳转的时候 内存不会释放 一直占用你的CPU 只有当你关闭了浏览器才会被释放);

  2、闭包会在父函数外部改变父函数内部的变量的值,所以不要随便改动父函数内部的值。

更多参考资料:

  《Javascript高级程序设计(第三版)》第四章、第七章

  《Javascript面向对象编程指南》第三章

作者的话:

  这篇文章主要先是通过几个简单的例子介绍作用域链(顺便补充了几个和作用域链相关的易出错的小知识),然后通过提问慢慢过渡到闭包(在闭包这部分介绍了几种常见闭包的例子),后面又进一步讲到了关于闭包的更高级的用法。后面遇到关于闭包的较好的用法会继续更新。

时间: 2024-10-10 23:46:58

Javascript中的闭包(转载)的相关文章

javascript中的闭包解析

学习javaScript已经有一段时间了,在这段时间里,已经感受到了JavaScript的种种魅力,这是一门神奇的语言,同时也是一门正在逐步完善的语言,相信在大家的逐步修改中,这门语言会逐步的完善下去,在上一篇随笔中,和大家分享了JavaScript中独有的类中的继承方式,今天呢,就跟大家分享一下我这几天一直在搞,却还是搞的不是很透彻的闭包问题,对于一个初学者而言,JavaScript中的闭包无疑是一个难点,而且也是我们必须要掌握的一个重点,那么今天我就跟大家分享一下我在学习闭包的时候的感悟以及

javascript中的闭包。

function todo() { var var1 = 1; (function () { var var2 = var1 + 1; alert(var2); })(); } todo(); (function(){})()是javascript里的闭包.可以在这个里面调用外面的js变量.但是外面的js变量不能调用里面的变量. javascript中的闭包.,布布扣,bubuko.com

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

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

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

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

浅析 JavaScript 中的闭包(-------------------------------------------)

一.前言 对于 JavaScript 来说,闭包是一个非常强大的特征.但对于刚开始接触的初学者来说它又似乎是特别高深的.今天我们一起来揭开闭包的神秘面纱.闭包这一块也有很多的文章介绍过了,今天我就浅谈一下自己对闭包的的一些理解,希望能提供一点鄙陋的见解帮助到正在学习的朋友.该文章中能使用口语化的我将尽量使用口语化的叙述方式,希望能让读者更好理解,毕竟文章写出来宗旨就是要让人读懂.文章难免有不足之处还希望帮忙指出. 二.Javascript 的作用域链 在了解闭包之前,我们先来看看几个准备知识.

浅析 JavaScript 中的闭包(Closures)

一.前言 对于 JavaScript 来说,闭包是一个非常强大的特征.但对于刚开始接触的初学者来说它又似乎是特别高深的.今天我们一起来揭开闭包的神秘面纱.闭包这一块也有很多的文章介绍过了,今天我就浅谈一下自己对闭包的的一些理解,希望能提供一点鄙陋的见解帮助到正在学习的朋友.该文章中能使用口语化的我将尽量使用口语化的叙述方式,希望能让读者更好理解,毕竟文章写出来宗旨就是要让人读懂.文章难免有不足之处还希望帮忙指出. 二.Javascript 的作用域链 在了解闭包之前,我们先来看看几个准备知识.

浅谈javascript中的闭包

引入定义:闭包只有权访问另一个函数中的作用域中的函数. 简单点说,就是当某函数a执行完毕后,闭包不会使得GC(JavaScript的回收机制)去回收a所占用的资源,因为a的内部函数b的执行需要依赖a中的变量. 代码示例: window.onload = function(){ function createComparisonFunction(propertyName){ return function(object1, object2){ var value1 = object1[proper

JavaScript中的闭包(Closure)

在上一篇介绍JavaScript this 关键字的文章中我们提到了闭包这个概念.闭包是指有权访问另一个函数作用域中的变量的函数.从函数对象中能够对外部变量进行访问(引用.更新),是构成闭包的条件之一.创建闭包的常见方式,就是在一个函数内部创建另一个函数.为了理解闭包,先来看一下什么是变量的生命周期. 变量的声明周期,就是变量的寿命,相对于表示程序中变量可见范围的作用域来说,生命周期这个概念指的是一个变量可以在多长的周期范围内存在并能够被访问.看下面一个例子: functionextent() 

Javascript中的闭包 O__O &quot;…

一.闭包!? 闭包(closure)是Javascript语言的一个难点,对于初学者来说不容易理解,那我们先来看看闭包的含义. 百度百科与"官方"解释:所谓"闭包",指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. 维基百科:在编程语言中,闭包(也称为词法闭包或函数闭包)是用于在具有第一类函数的语言中实现词法范围名称绑定的技术. 然而我们看到这些官方的解释并不能很清楚的了解闭包到底是什么鬼东西,只会一头问号,