资讯专栏INFORMATION COLUMN

闭包的探索

he_xd / 1490人阅读

摘要:在不知道闭包之前,因为链式作用域的关系只能向上一级的作用域查找变量,我一直认为函数内的变量在函数之外是无法访问到的。其实闭包的理解很简单,闭包就是能够取得函数内部变量的函数。在本质上闭包其实就是链接函数内部与函数外部的桥梁。

在不知道闭包之前,因为链式作用域的关系(只能向上一级的作用域查找变量),我一直认为函数内的变量在函数之外是无法访问到的。直到认识了你--闭包,让我彻底颠覆了之前的想法,也让我明白了不要随便下结论,要不你会死的很惨……
谈到闭包或许会让你想到匿名函数,因为这两个神奇的小怪物会常常让人混淆。闭包呢是指有权访问另一个函数作用域中的变量的函数,我们创建闭包最常用的方法就是在一个函数内部创建另一个函数,以下面为例就是一个最简单的闭包:

function f(){
    var num=10;
    function f1(){
        return num;
    }
    return f1;
}
var result=f(); 
console.log(result());  //10

1、作用域

谈论闭包之前呢还是需要想回顾一下变量的作用域问题。
作用域无非就是全局变量和局部变量。
这样有引出了作用域链,在作用域链只能向上一级查找变量,所以说在函数内部我们可以直接读取全局变量,但在函数外部是无法读取函数内部的局部变量的。
我们都知道函数的执行依赖于变量作用域,这个作用域是在函数定义时候决定的,而不是在函数调用时决定的。看下面例子:

var scope="global scope";
function checkscope(){
    var scope="local scope";
    function f(){
        return scope;
    }
    return f;
}
checkscope()();    //local scope

嵌套的函数f()是定义在函数内部的,其中的变量scope是局部变量,不管在何时何地执行f(),作用域链是不会改变的。

var num1=10;
function f1(){
    var num2=20;
    num3=30;
    alert(num1);
}
f1();  //10
alert(num1);  //10
alert(num2);  //num2 is not defined
alert(num3);  //30

这里有一个需要注意的地方,函数内部声明变量一定要使用var,如果一不小心忘记了,那对不起了,你将声明一个全局变量!

2、如何在函数外部读取到函数内部的变量呢?

但是在某种情况下因为一些原因我们是需要得到函数内部的局部变量,但是前面我们也尝试过了,在函数外部是无法得到内部的变量的,怎么办,这可愁死宝宝了……别急,办法还是有的:

function f(){
    var num=10;
    function f1(){
        return num++;
    }
    return f1;
}
var count=f();
count();  //10
count();  //11
count();  //12
count();  //13
count();  //15

正如刚开始我们的例子,f1被包含在了f内部,此时f的变量对于f1都是所谓的上一级,也就是都是可见的,随时都可以访问。既然f1可以读取f中的局部变量,那我们只需把f1作为返回值即可在f外部取得f的局部变量了。

3、到底什么是闭包?

其实上一段代码中的f1函数就是我们说的闭包。其实闭包的理解很简单,闭包就是能够取得函数内部变量的函数。
在本质上闭包其实就是链接函数内部与函数外部的桥梁。

4、闭包的用途

说了好多,但是闭包到底有什么用,为什么要学习这个“违法”的怪物?闭包的用处实在是太多了,最最重要的就是:可以在函数外部读取函数的变量,另一个就是让这些变量的值永远保存在内存中。what?莫急,请看下面代码:

function f(){
    var num1=99;
    add=function(){
        num1+=1;
    }
    function f1(){
        return num1;
    }
    return f1;
}
var result=f();
alert(result());  //99
add();
alert(result());  //100

为什么num1没有在result()调用之后被清楚呢?原因就是f是f1的父函数,f1最后是被赋值给了全局变量result,这导致了f1会一直在内存当中,然而f1的存在是依赖于f的,因此f也将一直在内存中。
此处有玄机哦,注意add函数前面没有var关键字,也就是说add现在是一个全局变量,而add的值是一个匿名函数(另一个小怪物),而这个匿名函数本身也是一个闭包(因为他访问了局部变量num1),他可以在函数外部对函数内部的变量进行操作。

5、闭包里的this

我们都知道,this对象是在运行时基于函数的执行环境绑定的:全局作用域下,this就是window,而当函数作为某个对象的方法调用时,this等于那个对象(其实在全局作用于下,函数就是window的方法)。不过匿名函数的执行环境是具有全局性的,因此this通常指向window。

var name="the window";
var object={
    name:"my object",
    getNameFunc(){
        return function(){
            return this.name;
        }
    }
}
alert(object.getNameFunc()());  //"the window"

但不全是:

var name="the window";
var object={
    name:"my object",
    getNameFunc:function(){
        var that=this;
        return function(){
            return that.name;
        };
    }
}
alert(object.getNameFunc()());  //"my object"

6、使用闭包要小心

1)我们上面提到过闭包可以让变量永久保存在内存中,这会导致内存消耗过大,所以使用闭包要谨慎,否则会带来性能问题,IE中会导致内存泄漏。

function assigmHandler(){
    var element=document.getElementById("myelement");
    element.onclick=function(){
        alert(element.id);
    }
}

此后element将永远驻留在内存中。解决办法就是在退出函数之前把不是用的局部变量删除。

function assigmHandler(){
    var element=document.getElementById("myelement");
    var id=element.id;
    element.onclick=function(){
        alert(id);
    }
    element=null;
}

2)闭包会在父函数外部改变父函数的变量,比如上面的例子:

function outer() {
    var obj = {
        name: "xiaoming"
    }
    return {
        number: obj,
        getObj: function () {
            console.log(obj)
        }
    }
}
var people = outer();
people.getObj();   //Object {name: "xiaoming"} 
people.number.name = "xiaozhang";
people.getObj();  //Object {name: "xiaozhang"}

如果你把父函数当作对象使用,把闭包当作它的公用方法,把内部变量当作它的私有属性,这时一定要小心,不要随便改变父函数内部变量的值。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/86516.html

相关文章

  • javascript闭包不完全探索记录02:闭包?干嘛使!

    摘要:温馨提示作者的爬坑记录,对你等大神完全没有价值,别在我这浪费生命温馨提示续本文将会成为一篇笔记类型的文章,记录闭包具体的应用方式温馨提示再续本文存在错误,会慢慢改进的,请不要把我说的当真在上一篇博文闭包不完全探索记录闭包啥馅的中,对中 温馨提示:作者的爬坑记录,对你等大神完全没有价值,别在我这浪费生命温馨提示-续:本文(maybe)将会成为一篇笔记类型的文章,记录闭包具体的应用方式温馨...

    Render 评论0 收藏0
  • javascript闭包不完全探索记录01:闭包?啥馅

    摘要:闭包一词来源于以下两者的结合要执行的代码块由于自由变量被包含在代码块中,这些自由变量以及它们引用的对象没有被释放和为自由变量提供绑定的计算环境作用域。在以及及以上等语言中都能找到对闭包不同程度的支持。 温馨提示:作者的爬坑记录,对你等大神完全没有价值,别在我这浪费生命 闭包,好吃吗 ? 第一次听到这个词,很不幸是在一次面试中,可想而知结果很细碎,从此闭包和跨域在我匮乏的前端知识中成为了...

    TANKING 评论0 收藏0
  • javascript对象不完全探索记录01:this! which?

    温馨提示:作者的爬坑记录,对你等大神完全没有价值,别在我这浪费生命 这一切,源于阮大神博文学习Javascript闭包(Closure)- 阮一峰中的一道思考题 //问题1: var name = The Window; var object = {   name : My Object,   getNameFunc : function(){     return function(){    ...

    rubyshen 评论0 收藏0
  • 探索Javascript设计模式---单例模式

    摘要:单例模式主要是为了解决对象的创建问题。页面中只放一个按钮登录实现得到登录框元素绑定事件关闭弹框这里做登录点击页面中的按钮每次让登录框出现即可上面的代码根据单例模式的使用构造函数来实现的。 最近打算系统的学习javascript设计模式,以便自己在开发中遇到问题可以按照设计模式提供的思路进行封装,这样可以提高开发效率并且可以预先规避很多未知的问题。 先从最基本的单例模式开始。 什么是单例...

    Hanks10100 评论0 收藏0
  • 单例模式 | 程序员都想要探索 Javascript 设计模式

    摘要:单例模式主要是为了解决对象的创建问题。页面中只放一个按钮登录实现得到登录框元素绑定事件关闭弹框这里做登录点击页面中的按钮每次让登录框出现即可上面的代码根据单例模式的使用构造函数来实现的。 showImg(https://segmentfault.com/img/bVbiE4g?w=568&h=450);最近打算系统的学习 Javascript 设计模式,以便自己在开发中遇到问题可以按照...

    rottengeek 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<