JavaScript 作用域和闭包
作用域的嵌套将形成作用域链,函数的嵌套将形成闭包。闭包与作用域链是 JavaScript 区别于其它语言的重要特性之一。
作用域
JavaScript 中有两种作用域:函数作用域和全局作用域。
在一个函数中声明的变量以及该函数的参数享有同一个作用域,即函数作用域。一个简单的函数作用域的例子:
function foo() {
var bar = 1;
{
var bar = 2;
}
return bar; //
}
不同于C等其它有块作用域的语言,这里将始终返回 2
。
全局作用域,对于浏览器来说可以理解为 window
对象(Node.js则是 global
):
var bar = 1;
function foo() {}
alert(window.bar); //
alert(window.foo); // "function foo() {}"
对于变量 bar
和函数 foo
都属于全局作用域,都是 window
的一个属性。
作用域链
在 JavaScript 中访问一个变量时,将从本地变量和参数开始,逐级向上遍历作用域直到全局作用域。
var scope = 0, zero = "global-scope";
(function(){
var scope = 1, one = "scope-1";
(function(){
var scope = 2, two = "scope-2";
(function(){
var scope = 3, three = "scope-3";
// scope-3 scope-2 scope-1 global-scope
console.log([three, two, one, zero].join(" "));
console.log(scope); //
})();
console.log(typeof three); // undefined
console.log(scope); //
})();
console.log(typeof two); // undefined
console.log(scope); //
})();
console.log(typeof one); // undefined
console.log(scope); //
在最里层的函数中,各个变量都能被逐级遍历并输出。而倒数第二层的函数中,变量 three
无法遍历找到,所以输出了 undefined
。
举一个通俗点的例子,你准备要花钱买点东西时,会先摸摸自己的钱包,没了你可以找你爸要,你爸也没有就再找你爷爷,... 。而你爸没钱买东西时,他并不会来找你要。
闭包
在一个函数中,定义另一个函数,称为函数嵌套。函数的嵌套将形成一个闭包。
闭包与作用域链相辅相成,函数的嵌套在产生了链式关系的多个作用域的同时,也形成了一个闭包。
function bind(func, target) {
return function() {
func.apply(target, arguments);
};
}
那么怎么理解闭包呢?
- 外部函数不能访问内嵌函数
- 外部函数也不能访问内嵌函数的参数和变量
- 而内嵌函数可以访问外部函数的参数和变量
- 换一个说法:内嵌函数包含了外部函数的作用域
我们再看看之前讲述的作用域链的例子,这次从闭包的角度来理解下:
var scope = 0, zero = "global-scope";
(function(){
var scope = 1, one = "scope-1";
(function(){
var scope = 2, two = "scope-2";
(function(){
var scope = 3, three = "scope-3";
// scope-3 scope-2 scope-1 global-scope
console.log([three, two, one, zero].join(" "));
console.log(scope); //
})();
console.log(typeof three); // undefined
console.log(scope); //
})();
console.log(typeof two); // undefined
console.log(scope); //
})();
console.log(typeof one); // undefined
console.log(scope); //
最里层的函数能访问到其内部和外部定义的所有变量。而倒数第二层的函数无法访问到最里层的变量,同时,最里层的 scope = 3
这个赋值操作并没有对其外部的同名变量产生影响。
再换个角度来理解闭包:
- 每次外部函数的调用,内嵌函数都会被创建一次
- 在它被创建时,外部函数的作用域(包括任何本地变量、参数等上下文), 会成为每个内嵌函数对象的内部状态的一部分,即使在外部函数执行完并退出后
看下面的例子:
var i, list = [];
for (i = 0; i < 2; i += 1) {
list.push(function(){
console.log(i);
});
}
list.forEach(function(func){
func();
});
我们将得到两次 "2" ,而不是预期的 "1" 和 "2" ,这是因为在 list
中的两个函数访问的变量 i
都是其上一层作用域的同一个变量。
我们改动下代码,以利用闭包来解决这个问题:
var i, list = [];
for (i = 0; i < 2; i += 1) {
list.push((function(j){
return function(){
console.log(j);
};
})(i));
}
list.forEach(function(func){
func();
});
外层的“立即执行函数”接收了一个参数变量 i
,在其函数内以参数 j
的形式存在,它与被返回的内层函数中的名称 j
指向同一个引用。外层函数执行并退出后,参数 j
(此时它的值为 i
的当前值)成为了其内层函数的状态的一部分被保存了下来。
JavaScript 作用域和闭包的更多相关文章
- javascript作用域和闭包之我见
javascript作用域和闭包之我见 看了<你不知道的JavaScript(上卷)>的第一部分--作用域和闭包,感受颇深,遂写一篇读书笔记加深印象.路过的大牛欢迎指点,对这方面不懂的同学 ...
- JavaScript 作用域和闭包——另一个角度:扩展你对作用域和闭包的认识【翻译+整理】
原文地址 --这篇文章有点意思,可以扩展你对作用域和闭包的认识. 本文内容 背景 作用域 闭包 臭名昭著的循环问题 自调用函数(匿名函数) 其他 我认为,尝试向别人解释 JavaScript 作用域和 ...
- JavaScript作用域和闭包
在JavaScript中,作用域是执行代码的上下文.作用域有3种类型: 1.全局作用域 2.局部作用域---(又叫函数作用域) 3.eval作用域 var foo =0;//全局作用域console. ...
- 举例详细说明javascript作用域、闭包原理以及性能问题(转)
转自:http://www.cnblogs.com/mrsunny/archive/2011/11/03/2233978.html 这可能是每一个jser都曾经为之头疼的却又非常经典的问题,关系到内存 ...
- javascript作用域、闭包、对象与原型链
原文作者总结得特别好,自己收藏一下.^-^ 1.作用域1.1函数作用域JS的在函数中定义的局部变量只对这个函数内部可见,称之谓函数作用域.它没有块级作用域(因此if.for等语句中的花括号不是独立作用 ...
- javascript作用域与闭包
Javasript作用域概要 在javascript中,作用域是执行代码的上下文,作用域有三种类型: 1) 全局作用域 2) 局部作用域(函数作用域) 3) eval作用域 var foo = ...
- JavaScript作用域与闭包总结
1.全局作用域 所有浏览器都支持 window 对象,它表示浏览器窗口,JavaScript 全局对象.函数以及变量均自动成为 window 对象的成员.所以,全局变量是 window 对象的属性,全 ...
- javascript——作用域与闭包
http://www.cnblogs.com/lucio-yr/p/4047972.html 一.作用域: 在函数内部:用 var 声明的表示局部变量,未用var的是全局变量. 作用域取决于变量定义时 ...
- 《JavaScript 闯关记》之作用域和闭包
作用域和闭包是 JavaScript 最重要的概念之一,想要进一步学习 JavaScript,就必须理解 JavaScript 作用域和闭包的工作原理. 作用域 任何程序设计语言都有作用域的概念,简单 ...
随机推荐
- PrepareCommand
/// <summary> /// 执行参数查询 /// </summary> /// <param name="cmd">数据库执行命令< ...
- Lintcode--004(最小子串覆盖)
给定一个字符串source和一个目标字符串target,在字符串source中找到包括所有目标字符串字母的子串. 注意事项 如果在source中没有这样的子串,返回"",如果有多个 ...
- Mysql 新建用户以及授权远程连接操作
1:以root身份登陆mysql终端 mysql -uroot -pmysql 2:创建wx用户,注意密码要加单引号 mysql> create user wx identified by 'w ...
- Android项目使用support v7时遇到的各种问题
Android项目使用support v7时遇到的各种问题 点击你的工程右键-->Properties-->Android 1.查看你引用的appcompat_v7包是否引用正确 2.用较 ...
- jQuery自定义函数验证邮箱格式
jQuery.fn.checkEmail = function() { // 自定义jQuery方法 var email_val = $(this).val(); reg = /^\w+([-+.]\ ...
- NOI2015
D1T1 并查集. #include<cstdio> #include<cstdlib> #include<iostream> #include<fstrea ...
- requirejs-define jquery 快速初学实例(一)
原文地址:http://6yang.net/articles_view.php?id=1103 2011-10-18 13:12:01 by [6yang], 1029 visits, 收藏 | 返回 ...
- POJ1061青蛙的约会(扩展欧几里德算法)
青蛙的约会 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 102239 Accepted: 19781 Descript ...
- PHP常用魔术方法(__call魔术方法:)
魔术方法 __call <?php //文件名:index.php define('a',__DIR__); include '/IMooc/Loader.php'; spl_autoload ...
- 小米路由器mini建FTP
输入命令 df -h 复制代码 查看自己的分区路径,我自己用了一个闲置笔记本电脑硬盘转USB,插到mini,有四个区 为了方便,我选择把ftp安装在第一个分区 /extdisks/sda1/ 创建f ...