介于之前在网上看到很多关于javascript闭包的问题,今天刚好在学习vue的时候也用到了,借此机会做一个笔记,留下来以后也好重新复习一下,巩固一下基础知识;
介绍一个东西之前,还是先要了解一下这是个什么?
在认识闭包之前,需要了解一个变量作用域的问题;
变量作用域无非是两种:全局变量和局部变量;
var a='koala';
function f(){
var b='alecor';
alert(b);
}
f();//alert(b)==>alecor
简单的列子:那么这里的’a‘就是全局变量,‘b’就是局部变量
最后会弹出 ”b“这个变量的值;(这里有必要提一下,就是alert这个函数它是一个同步函数,这就意味着它单击确定后,其后的操作才能进行,否则会一直等待下去)
function f1(){
var n=999;
}
alert(n); // error
但是你们看,下面这种情况他就不会出现错误,原因是找不到”n”这个变量(这里有一个地方需要注意,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!)
function f1(){ n=999; } f1();
alert(n); // 999,
问题来了,如何才能访问内部变量呢???
其实也很简单,我们可以把内部变量当作函数的返回值传递出去,这样外部函数就可以范围访问内部变量了;
function f1(){
var n=999;
function f2(){
alert(n)
}
return f2;
}
var result=f1();
result(); // 999
其实这里面的 f2()函数,就是一个闭包
我们来看看它是如何定义的:
闭包是指可以包含自由(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)(来自百度)
这样理解肯定很晦涩难懂,我的理解是,闭包就是能够读取其他函数内部变量的函数。
我们接下来再看一下例子(可能跨度有些大)
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()());
其实这道题是我当初我去面试JavaWeb工程师的时候,碰到的一个题,当初就没系统的学习javascript,一直到现在才慢慢把javascript系统的学习,毕竟自己以后是想从事一个全栈,期待全栈后面的世界又会有什么在等着我???
接着看这个题目,
我们来分析一下,为了方便 我直接在备注中写分析
var name = "The Window"; 全局变量
var object = { //对象,拥有name属性和getNameFunc函数
name : "My Object", //内部变量
getNameFunc : function(){
return function(){ //第一层返回作用域(object对象)
return this.name; //第二层返回作用域(window对象)
};
}
};
alert(object.getNameFunc()());
这个函数最后输出的是最外一层,因为它的作用域是全局对象,因此调用最外一层的name属性
让我们在看这个列子:
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()());
在这个示例里面, 我们把object对象的this,赋值给that对象,那么此时that对象存在getNameFunc函数的局部变量,因此最后输出的回事 "myObject"
接着我们看一个关于计时器闭包的问题;
for(var i=0;i<5;i++){ setTimeout(function(){ alert(i); },i*100);
}
在上述的代码中,你认为他会输出什么,通常来说你肯定以为他会输出,0,1,2,3,4 (间隔一秒钟)
但是其实这是错的,他会连续输出5个5为什么呢???
我们仔细阅读代码看看
setTimeout(function(){
alert(i);
},i*100);
在这段定时器的代码中,你能找到的的参数吗? 里面的alert(i)这个i变量不就是得吗???其实并不然;
我们知道函数是可以传递参数的,但是在这个定时器的函数里面它没有给形参,也就是说定时器在调用这个函数的时候它是没有参数的,也可以说他没有局部参数;因此这里的函数仅仅只做在定时器里面的函数声明使用,并没有调用;
所以最后他会输出 5 5 5 5 5,
这里还有一点就是 定时器它是一个异步函数,它必须要等线程队列中没有主线程才会执行它自身,或者说是等队列里面的主线程执行完了,定时器才会作用;所以等for循环走完之后,定时器才开始计时,由于Javascript语言特有的”链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量;所以for循环完之后,找到它的上一级i,也就是5,因此他会连续输出5个5;(需要好好理解)
那么怎么让他一次输出0-1-2-3-4呢???,我这里给出两种方式参考
第一种:函数当作结果值返回
var createFunction=function(i){
return function(){
alert(i);
}
}
for(var i=0;i<5;i++){ setTimeout(createFunction(i),i*1000); }
每一次运行的时候都会给函数传递一个值,此时函数执行完后会返回一个值;
第二种:自执行函数,
for(var i = 0; i < 5; i++) {
setTimeout(function(i) {
alert(i)
}(i), i * 1000);
}
如何怎么理解自执行函数呢??传送门:[自执行函数](http://blog.csdn.net/limlimlim/article/details/9198111)
第三种
for(var i = 0; i < 5; i++) {
(function(i) {
setTimeout(function() {
alert(i)
}, i * 1000);
})(i)
}
这两种方式都是自执行函数,一种放在计时器里面使用,一种放在计时器外面使用;
好了,今晚就介绍这么多,接下来去学习vue2.0了,果然代码是敲出来的,自己思路更加清晰了很多;