您的位置:首页 > Web前端 > JavaScript

JavaScript学习记录——闭包作用域

2014-04-06 16:00 513 查看
一、函数的执行顺序:

       1、通过function fn()这种写法来定义的函数,永远都会被最先初始化;

       2、以匿名函数方式定义的函数,不会被先执行,如果在之前调用该函数就会报错。

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>js01_hello</title>
<meta name="author" content="Administrator" />
<script type="text/javascript">
fn1();
//不会报错,对于通过function fn()这种写法来定义的函数,永远都会被最先初始化
function fn1() {
alert("fn1");
}

fn2();
//使用如下方式定义函数,不会被先执行,如果在之前调用该函数就会报错
/**
* 以下函数的定义方式是现在内存中创建了一块区域,之后通过一个fn2的变量
* 指向这块区域,这块区域的函数开始是没有名称的 ,这种函数就叫做匿名函数
*/
var fn2 = function() {
alert("fn2");
}
</script>
</head>
<body>
</body>
</html>


二、函数作用域链

        在js中当进行函数的调用,会为每一个函数增加一个属性SCOPE,通过这个属性来指向一块内存这块内存中包含有所有的上下文使用的变量,当在某个函数中调用了新函数之后,新函数依然 会有一个作用域来执行原有的函数的SCOPE和自己新增加的SCOPE,这样就形成一个链式结构这就是js中的作用域链。

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>js01_hello</title>
<meta name="author" content="Administrator" />
<script type="text/javascript">
/**
* 在js中当进行函数的调用,会为每一个函数增加一个属性SCOPE,通过这个属性来指向一块内存
* 这块内存中包含有所有的上下文使用的变量,当在某个函数中调用了新函数之后,新函数依然
* 会有一个作用域来执行原有的函数的SCOPE和自己新增加的SCOPE,这样就形成一个链式结构
* 这就是js中的作用域链
*/
var color = "red";

var showColor = function() {
alert(this.color);
}

function changeColor() {
var anotherColor = "blue";
function swapColor() {
var tempColor = anotherColor;
anotherColor = color;
color = tempColor;
}
swapColor();
}

changeColor();

showColor();
</script>
</head>
<body>
</body>
</html>


        这段代码的函数作用域链图如下所示:



三、函数的内存释放

       在JavaScript中,可以通过返回函数的方法,扩大函数的作用域的生命周期,而在Java、C++等语言在函数执行后,变量就会从内存中释放。

       但是这种方法将会使执行过程中占用更多的内存空间,一般非特殊情况,不要使用闭包。

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>js01_hello</title>
<meta name="author" content="Administrator" />
<script type="text/javascript">
/**
* 通过以下操作带来最大的好处是,compareObjectFunction的作用域变大了
* 当compareObjectFunction结束之后,prop这个变量依然存在
*/
function compareObjectFunction(prop) {
//匿名函数
return function(obj1,obj2) {
if(obj1[prop]>obj2[prop]) return 1;
else if(obj1[prop]<obj2[prop]) return -1;
else return 0;
}
}
var o1 = {name:"Leon",age:23};
var o2 = {name:"Ada",age:28};
//此时就是基于name来进行比较
/*
* 在java或者c++中,以下代码执行完成之后,需要进行内存的释放
* 此时对于java和c++这些静态语言而言,prop会被释放
* 但是在js中,这个作用域却被放大了
*/
var compare = compareObjectFunction("age");
//此时就比较了o1和o2
/*
* 在js中,prop在这里依然可以被访问,这种通过返回函数来扩大函数的作用域的方法
* 就是闭包
*/
var rel = compare(o1,o2);
alert(rel);
</script>
</head>
<body>
</body>
</html>

        这段代码的函数作用域链图如下所示:



四、闭包的作用域

1、作用域保存到上一级函数中:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>js01_hello</title>
<meta name="author" content="Administrator" />
<script type="text/javascript">
function fn1() {
//创建了一个数组
var fns = new Array();
//i这个变量是保存在fn1这个作用域中的
for(var i=0;i<10;i++) {
//数组中方的值是一组函数
fns[i] = function() {
return i;
}
}
return fns;
}

var fs = fn1();
for(var i=0;i<fs.length;i++) {
//此时通过闭包来调用所有函数,当输出i的时候会去上一级的作用域中查找
//这个时候i的值已经10,所以连续输出了10个10
document.write(fs[i]()+"<br/>");
}
</script>
</head>
<body>
</body>
</html>


2、作用域保存在下一级每一个子函数中:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>js01_hello</title>
<meta name="author" content="Administrator" />
<script type="text/javascript">
function fn1() {
//创建了一个数组
var fns = new Array();
//i这个变量是保存在fn1这个作用域中的
for(var i=0;i<10;i++) {
//num这个变量是保存在fns这个tf这个作用域,每一个闭包的num都是不一样
//所以此时所消耗的内存特别的大
var tf = function(num) {
fns[num] = function() {
return num;
}
}
tf(i);
}
return fns;
}

var fs = fn1();
for(var i=0;i<fs.length;i++) {
//每一个fs都是在不同作用域链中,num也是保存在不同的作用域中,所以输出0-9
document.write(fs[i]()+"<br/>");
}
</script>
</head>
<body>
</body>
</html>


五、闭包中this的问题:

        调用函数时,this是指向类的,但是调用结束后,再调用里面的匿名函数的时候,this就指向window:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>js01_hello</title>
<meta name="author" content="Administrator" />
<script type="text/javascript">
var name = "window";
var person = {
name:"zhangsan",
age:23,
say:function() {
return function() {
return this.name;
}
}
}
/*
* 当完成person.say()之后,这个函数就调用结束了,在这个函数调用结束之前
* this是指向person,但是在调用匿名函数的时候,this就指向window,所以
* 得到的结果是window
*
*/
alert(person.say()());
</script>
</head>
<body>
</body>
</html>


解决的方案是使用一个中间变量来保存this:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>js01_hello</title>
<meta name="author" content="Administrator" />
<script type="text/javascript">
var name = "window";
var person = {
name:"zhangsan",
age:23,
say:function() {
//that就指向person
var that = this;
return function() {
return that.name;
}
}
}
/*
* 此时that是指向person的,所以调用that.name就是person中name
*
*/
alert(person.say()());
</script>
</head>
<body>
</body>
</html>

六、块作用域

       JavaScript没有块作用域,这样会导致大量的全局变量,在一个大型项目开发中,很有可能会由于变量名称相同会出现不可预见的错误,解决的方案是采用匿名函数的方法。

       例如下面的代码中,循环代码块中使用的变量i,会影响到后面使用i作为的变量的代码:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>js01_hello</title>
<meta name="author" content="Administrator" />
<script type="text/javascript">
for(var i=0;i<10;i++) {

}
//在js中没有块作用域,不管是使用循环还是判断之后,这个变量会一直存在
/*
* 所以当在全局使用某个变量进行循环或者判断之后,这个变量可能会影响
* 到函数中的变量,所以在特殊情况不要使用全局变量,而且使用全局变量
* 在作用域链的最上层,访问是最慢的
*/
var i;//此时会认为是无效语句,除非使用var i = 0;
alert(i);
</script>
</head>
<body>
</body>
</html>


下面的代码是使用匿名函数的例子:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>js01_hello</title>
<meta name="author" content="Administrator" />
<script type="text/javascript">
/*
* 在一个团队进行开发时,可能会涉及到定义同名的全局变量,所以在开发中
* 一定养成如下习惯,将全局变量的代码放到一个匿名函数,并且马上调用
* 匿名函数,这样也可以执行全局变量的代码,但是这些变量就被控制在开发
* 人员想要控制的作用域中了
*/
//在function的{}后不能直接调用,一定要加上括号
// function(){
// for(var i=0;i<10;i++) {
//
// }
// }();
(function(){
for(var i=0;i<10;i++) {

}
})();

function fn() {
alert(i);
}
fn();
</script>
</head>
<body>
</body>
</html>

七、私有变量

       私有变量可以在匿名函数中定义,这样外面就无法直接访问了。

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>js01_hello</title>
<meta name="author" content="Administrator" />
<script type="text/javascript">
var Person;
(function(){
//name正在函数结束之后就消失,在外面是无法引用的
var name = "";
Person = function(value){
name = value;
}
Person.prototype.setName = function(value) {
name = value;
}
Person.prototype.getName = function() {
return name;
}
})();

var p1 = new Person("aa");
alert(p1.getName());
p1.setName("bb");
alert(p1.getName());
</script>
</head>
<body>
</body>
</html>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  javascript 闭包 js