你理解Javascript的闭包吗?
最初接触到Javascript中闭包的概念时也不甚理解,现在百度一些概念名词想要找到一篇讲的比较透彻的文章还真是不容易,不过阮一峰老师的这篇《学习Javascript闭包(Closure)》可以算的上是一篇好文章了。这里不想完全照搬此一佳作,毕竟不是自己的理解,但是有一些地方很是值得参考。首先我先引用阮一峰老师在这篇文章最后留给读者的最后两个问题。
问题一
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
var f = object.getNameFunc();
console.log(f());
问题二
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
var f = object.getNameFunc();
console.log(f());
当然这两个问题的运行结果只要运行一遍代码就可以得到,但是至于为什么是这样的结果我们稍后在做解释,首先我先说一说我对闭包的理解。
闭包——Closure
闭包的作用首先在其名字上面我们就可以推断它涉及到的知识点应该是和作用域有关,Closure本身就是封闭的意思,而在Javascript中变量的作用域可以说是封闭的,除此之外函数内部变量对于外部是不可见的,同样可以看作是一个封闭的环境,那如何访问到内部的变量,这就需要内部函数来帮忙了,因此我们可以说闭包肯定和这一问题有关。
下面我们看一个例子
代码一
function func(){
var n = 10;
function shown(){
console.log(n);
}
return shown;
}
var f = func();
f();
代码二
function func(){
var n = 10;
return n;
}
console.log(func());
我们看这两段代码执行的结果是一样的,都是打印出n的值为10。虽然结果相同,但是其内部运行机制简直是大相径庭。
首先我们看代码二,每次运行func()的时候,其内部变量n都会被初始化重新分配内存并且赋值。当func()运行结束,n就会被销毁。这点同其他编程语言的自定义函数的使用是相同的。
而对于代码一,它的运行机制是func()运行的时候,会初始化n,这一点和代码二相同。然后其内部创建一个内部函数shown,这个内部函数shown保存了本函数所在作用域的环境,这个环境包括任意一个在这个作用域中被创建的变量,当然在本例中是变量n。所以说每次调用f()都会打印出结果10,但是n却不用每次都初始化,因为n是一直保存在内存中的。当然这不是凭空说的,我们看shown函数可以被看作一个封闭的环境,里面保存了n的信息,但是这个n是由func创建的,并不是shown()的内部变量,在其内部只不过是保存有它的地址信息而已,如果说shown运行完毕就销毁这些信息的话,那它下次再运行的时候其内部变量n的信息是哪里来的呢,这些信息是他是如何得到的呢,其实无非是这些信息一直保存在内存中,n变量也一直在内存中并没有随着func()运行的结束而被销毁。如果被销毁了,当我们再次调用f()的时候打印出的将不是10而是undefined了。我们可以通过多次调用f()来检验是否结果都是10,事实和我们预想的一样。
(上面这段话不知道大家能不能理解,可能语言有些糙,但是我觉得应该还算清晰吧,如果有什么问题可以留言讨论)
通过以上的介绍,我们大概可以知道闭包就是解决函数内部变量访问的问题并且使变量常驻内存,二者缺一不可叫作闭包。也就是说,在上面的代码一中,shown就可以被叫作闭包。
变量作用域
既然闭包涉及到了函数内部变量访问的问题,那变量作用域是我们必须要理解的,关于Javascript变量作用域的问题,我之前写了一篇文章《你了解Javascript中潜藏在变量中的那些陷阱吗》在这篇文章中可以对变量的作用域有一个清楚的了解。
对闭包的概念和变量作用域有一定的了解以后,我们再回过头来看最开始的那两个问题。
对开头两个问题的解释
对于问题一其结果是“The Window”,因为虽然说object对象中定义了属性name,但是return function(){ return this.name;}中的this对象并不是指向object,因为object.getNameFunc()执行的过程中并没有this对象初始化,f()函数由‘上帝’对象调用,所以说闭包中保存的this对象的信息不是object的信息,因此this.name当然也不是指的object的name,而是全局变量name。
对于问题二其结果是“My Object”,因为在getNameFunc中有对that进行了初始化,因为object.getNameFunc()这表示是由object对象调用,所以var that = this;中的this指向的是object,所以that同样也指向了object对象,所以闭包f()中保存的对象信息是object,因此that.name指的是object.name所以打印出来的也就是“My Object”。
不知道这样说大家能不能理解,对于闭包的问题网上说法很多,有的说是外部函数才是闭包,有的说是内部函数是闭包,大家在这篇文章中得到的答案更倾向于后者,个人觉得前者是错误的说法。不论是外部函数也好还是内部函数也好,其闭包的作用应该是相同的,这一点肯定是没有问题的。
相关文章
Do you understand JavaScript closures?
发布时间:2025/02/21 浏览次数:108 分类:JavaScript
-
The function of a closure can be inferred from its name, suggesting that it is related to the concept of scope. A closure itself is a core concept in JavaScript, and being a core concept, it is naturally also a difficult one.
Do you know about the hidden traps in variables in JavaScript?
发布时间:2025/02/21 浏览次数:178 分类:JavaScript
-
Whether you're just starting to learn JavaScript or have been using it for a long time, I believe you'll encounter some traps related to JavaScript variable scope. The goal is to identify these traps before you fall into them, in order to av
How much do you know about the Prototype Chain?
发布时间:2025/02/21 浏览次数:150 分类:JavaScript
-
The prototype chain can be considered one of the core features of JavaScript, and certainly one of its more challenging aspects. If you've learned other object-oriented programming languages, you may find it somewhat confusing when you start
如何在 JavaScript 中合并两个数组而不出现重复的情况
发布时间:2024/03/23 浏览次数:86 分类:JavaScript
-
本教程介绍了如何在 JavaScript 中合并两个数组,以及如何删除任何重复的数组。