闭包,作用域链
- 变量对象
(什么是变量对象 -如果变量与执行上下文相关,那变量自己应该知道它的数据存储在哪里,并且知道如何访问。这种机制称为变量对象(variable object)。
有这样一个对象,存储着变量和对应的值。这个对象就叫做变量对象。)
(变量对象(缩写为VO)是一个与执行上下文相关的特殊对象,它存储着在上下文中声明的以下内容:
变量 (var, 变量声明);
函数声明 (FunctionDeclaration, 缩写为FD);
函数的形参)
变量对象VO (变量初始化过程的一般行为)
║
╠══> 全局上下文变量对象GlobalContextVO
║
╚══> 函数上下文变量对象FunctionContextVO
变量对象有什么作用
(存储着变量和对应的值。我们就可以知道如何访问这些变量和值。)
如何使用变量对象
在程序中,执行程序的时候,就可以看见变量对象,这样就可以知道如何访问这些变量和值。
同时我们也知道变量对象在每次进入上下文时创建,并填入初始值,值的更新出现在代码执行阶段。
- 活动对象
什么是活动对象 – 活动对象是进入函数中时对应的变量对象。
变量对象不可以直接访问,要通过活动对象才能访问。也就是对象激活了,才能访问。
- 作用域链
什么是作用域链
作用域链有什么用
try/catch
闭包
什么是闭包
为什么使用闭包
闭包提供了哪些特性
如何使用闭包
变量对象
什么是变量对象
变量对象:Variable Object, 是一个特殊对象,存储着以下内容:
变量 (var, 变量声明);
函数声明 (FunctionDeclaration, 缩写为FD);
函数的形参
为什么要使用变量对象
JavaScript编程的时候总避免不了声明函数和变量,以成功构建我们的系统,但是解释器是如何并且在什么地方去查找这些函数和变量呢,去变量对象中去找函数和变量。这样我们就可以访问到我们定义的函数和变量了。
什么时候会用到变量对象
(代码:由以下代码引出变量问题,这时就用到了变量对象
var a = 10; // 全局上下文中的变量
(function () {
var b = 20; // function上下文中的局部变量
})();
alert(a); // 10
alert(b); // 全局变量 ‘b’ 没有声明
)
如何使用变量对象
分析上面的例子, 从普通角度说一下。a 是全局变量,b是局部变量,所以访问不到b。
(代码:
var a = 10;
function test(x) {
var b = 20;
};
test(30);
对应的变量对象是:
// 全局上下文的变量对象
VO(globalContext) = {
a: 10,
test: <reference to function>
};
// test函数上下文的变量对象
VO(test functionContext) = {
x: 30,
b: 20
};
)
不同执行上下文中的变量对象
抽象变量对象VO (变量初始化过程的一般行为)
║
╠══> 全局上下文变量对象GlobalContextVO
║ (VO === this === global)
║
╚══> 函数上下文变量对象FunctionContextVO
(VO === AO, 并且添加了\<arguments\>和\<formal parameters\>)
全局上下文中的变量对象
全局对象(Global object) 是在进入任何执行上下文之前就已经创建了的对象;
这个对象只存在一份,它的属性在程序中任何地方都可以访问,全局对象的生命周期终止于程序退出那一刻。
(
举例:全局变量到处都可以访问
var a = 10;
function variableTest () {
document.write(a);
}
document.write(a);
document.write(‘<br>’);
variableTest ();
)
函数上下文中的变量对象
(代码解释什么是变量对象
var a = 10;
function test(x) {
var b = 20;
};
test(30);
// test函数上下文的变量对象
VO(test functionContext) = {
x: 30,
b: 20
};
这个就是函数上下文中的变量对象
)
活动对象
在函数执行上下文中,VO是不能直接访问的,此时由活动对象(activation object,缩写为AO)扮演VO的角色。
作用域链
抽象变量对象VO (变量初始化过程的一般行为)
║
╠══> 全局上下文变量对象GlobalContextVO
║ (VO === this === global)
║
╚══> 函数上下文变量对象FunctionContextVO
(VO === AO, 并且添加了\<arguments\>和\<formal parameters\>)
实际上这个图就可以看成是作用域链。
先去搜索函数上下文变量对象(此时由活动对象扮演VO的角色。),如果没搜到要使用的变量或者函数,那么就继续向上搜索,比如搜索全局上下文对象。就这样一层一层搜索,知道搜索到要使用的变量或者函数为止。
我们结合一些例子还理解作用域链。
(代码:
var x = 10;
function foo() {
var y = 20;
function bar() {
alert(x + y);
}
return bar;
}
foo()(); // 30
)
(代码:
var x = 10;
function foo() {
var y = 20;
function bar() {
var z = 30;
alert(x + y + z);
}
bar();
}
foo(); // 60
)
(代码:
name=’lwy’;
function t(){
var name='tlwy';
function s(){
var name='slwy';
console.log(name);
}
function ss(){
console.log(name);
}
s();
ss();
}
t();
)
闭包
什么是闭包
JS closure : A ‘closure’ is an expression (typically a function) that can have free variables together with an environment that binds those variables (that ‘closes’ the expression).
所谓’闭包’,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
闭包就是能够读取其他函数内部变量的函数。由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成’定义在一个函数内部的函数’。所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
为什么要使用闭包
保护函数内的变量安全。以最开始的例子为例,函数a中i只有函数b才能访问,而无法通过其他途径访问到,因此保护了i的安全性。
在内存中维持一个变量。依然如前例,由于闭包,函数a中i的一直存在于内存中,因此每次执行c(),都会给i自加1。
以上两点是闭包最基本的应用场景,很多经典案例都源于此。
计数器困境
设想下如果你想统计一些数值,且该计数器在所有函数中都是可用的。你可以使用全局变量,函数设置计数器递增:
var counter = 0;
function add() {
counter += 1;
}
add();
add();
add();
// 计数器现在为 3
计数器数值在执行 add() 函数时发生变化。
但问题来了,页面上的任何脚本都能改变计数器,即便没有调用 add() 函数。
如果我在函数内声明计数器,如果没有调用函数将无法修改计数器的值:
function add() {
var counter = 0;
counter += 1;
}
add();
add();
add();
// 本意是想输出 3, 但事与愿违,输出的都是 1 !
以上代码将无法正确输出,每次我调用 add() 函数,计数器都会设置为 1。
JavaScript 内嵌函数可以解决该问题。
所有函数都能访问全局变量。
实际上,在 JavaScript 中,所有函数都能访问它们上一层的作用域。
JavaScript 支持嵌套函数。嵌套函数可以访问上一层的函数变量。
该实例中,内嵌函数 plus() 可以访问父函数的 counter 变量:
function add() {
var counter = 0;
function plus() {counter += 1;}
plus();
return counter;
}
如果我们能在外部访问 plus() 函数,这样就能解决计数器的困境。
我们需要闭包。
如何使用闭包
JavaScript 闭包
var add = function () {
var counter = 0;
var aaa = 1;
function plus() {
counter += 1;
console.log('counter: ' + counter);
}
return plus;
};
var result = add();
result();
result();
result();
// 计数器为 3
result可以作为一个函数使用。非常棒的部分是它可以访问函数上一层作用域的计数器
这个叫作 JavaScript 闭包。 它使得函数拥有私有变量变成可能。
计数器受匿名函数的作用域保护,只能通过 plus 方法修改。
闭包是可访问上一层函数作用域里变量的函数,即便上一层函数已经关闭。
使用闭包的注意点:
- 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。