闭包与作用域
作用域
作用域: 程序定义变量的区域, 作用域中定义了如何找到对应的变量,因此在执行代码(运行在作用域中)时,能够获取对变量的访问权限
词法作用域 动态作用域
JavaScript属于词法作用域:作用域在定义时就已经确定,而非运行时确定。
- 静态作用域:作用域在定义时就已经确定
- 动态作用域:作用域在调用时确定
全局作用域 函数作用域
- 全局作用域 直接在脚本中书写的代码 在全局作用域中声明的变量,会被提升到脚本块的顶部,并且会称为全局对象的属性
- 函数作用域 在函数作用域中声明的变量,会被提升到函数的顶部,并且会成为函数的局部变量,并且不会成为全局对象的属性 因此,在函数作用域中声明的变量,只能在函数内部访问,不会造成全局对象的污染尽量将功能写在函数当中 但是,当函数成为一个表达式时,它既不会提升,也不会污染全局对象 将函数转为表达式的方式之一:是用小括号将函数包裹起来(表达式返回自身) 然而,这样做会导致一些问题,比如:
- 函数无法通过变量名调用
如果声明一个函数表达式,然后立即调用(function() {}())
,该函数称之为立即执行函数(IIFE),由于函数表达式的函数名没有什么意义,因此可以省略
作用域例子
javascript
var value =1;
function foo() {
console.log(value);
}
function bar() {
var value = 2;
foo();
}
bar(); // 1
javascript
// case 1
var scope = "global scope";
function checkScope() {
var scope = "local scope";
function f() {
return scope;
}
return f();
}
checkScope(); // "local scope"
// case 2
var scope = "global scope";
function checkScope() {
var scope = "local scope";
function f() {
return scope;
}
return f
}
checkScope()(); // "local scope"
执行上下文
全局执行上下文:所有JS代码执行之前,都必须有该环境 函数执行上下文:一个函数在运行之前,创建的一块内存空间,空间中包含该函数执行所需要的数据,为该函数执行提供支持 JS引擎始终执行的是栈顶的执行上下文,当遇到函数调用时,JS引擎会创建新的执行上下文,并将其推入栈顶,当函数执行完毕,JS引擎会将该执行上下文从栈顶弹出。
执行上下文栈
所有执行上下文组成的内存空间 栈:一种数据结构,先进后出,后进先出。
执行上下文的内容
this
指向当前执行上下文的执行环境,具体取值取决于函数调用方式
变量对象(variable object)
包含了在该环境中所有声明的参数、变量和函数
- 全局上下文的变量对象(global variable object):window(浏览器环境)或global(Node.js环境)
- 函数上下文的变量对象(activation object):包含了函数声明和函数参数
可执行代码(executable code)
- 全局代码
- 函数代码
- eval代码
执行函数执行上下文代码
AO(activation object):当前正在执行的上下文的变量对象(VO) GO(global object):全局执行上下文的变量对象(VO)
- 确定所有形参以及特殊变量
arguments
对象 - 确定函数中通过var声明的变量,将它们的值设为
undefined
,如果VO中已经有该名称,则直接忽略 - 确定函数中通过字面量声明的函数,将它们的值设为函数本身,如果VO中已经有该名称,则覆盖
javascript
function foo(a) {
var b = 2;
function c() {}
var d = function () {};
b = 3;
}
foo(1);
// 预编译阶段
AO = {
arguments: {
0: 1,
length: 1
},
a: 1,
b: undefined,
c: function c() {},
d: undefined
}
// 函数执行后
AO = {
arguments: {
0: 1,
length: 1
},
a: 1,
b: 2,
c: function c() {},
d: function () {}
}
- 例子
javascript
// 例子一
function foo() {
console.log(a);
a = 1;
}
foo(); // ReferenceError: a is not defined
function bar() {
a = 1;
console.log(a)
}
bar(); // 1
javascript
// 例子二
function A(a, b) {
console.log(a, b) // 1 function b () {}
var b = 123
function b() {}
var a = function () {}
console.log(a, b) // function () {}, 123
}
A(1, 2)
javascript
// 例子三
var foo = 1
function A(a, b) {
console.log(foo) // undefined
if (!foo) {
var foo = 10
}
console.log(foo) // 10
}
javascript
// 例子四
var a = 1
function b(a, b) {
console.log(a) // function a () {}
a = 10
return
function a() {}
}
b()
console.log(a) // 1
作用域链(scope chain)
- VO(variable object)变量对象中包含一个额外的属性,该属性指向创建该VO的函数本身
- 每个函数在创建时,会有一个隐藏属性
[[scope]]
,它指向创建该函数时的AO(当前的执行上下文) - 当访问一个变量时
- 先查找自身VO中是否存在,如果不存在,则依次查找
[[scope]]
属性
- 先查找自身VO中是否存在,如果不存在,则依次查找
javascript
// 例子
var g = 0;
function A() {
var a = 1;
function B() {
var b = 2;
var C = function () {
var c = 3;
console.log(g, a, b ,c)
}
C();
}
B();
}
A();
javascript
// 例子
function A() {
var count = 0;
return function () {
count++;
console.log(count)
}
}
var test = A();
test(); // 1
test(); // 2
test(); // 3
javascript
// 例子
var foo = { n: 1 }
(function (foo) {
console.log(foo.n) // 1
foo.n = 3;
var foo = { n: 2 }
console.log(foo.n) // 2
} (foo))
console.log(foo.n) // 3
javascript
// 例子
var a = 1;
function A() {
console.log(a);
}
function Special() {
var a = 5;
var B = A;
B();
}
Special()
javascript
// 例子
function foo() {
function bar() {}
}
foo['[[scope]]'] = [
globalContext.VO
]
bar['[[scope]]'] = [
fooContext.AO,
globalContext.VO
]
javascript
var scope = "global scope";
function checkScope() {
var scope2 = "local scope";
return scope2;
}
checkScope(); //
javascript
checkScope['[[scope]]'] = [
globalContext.VO
]
ECS = [
checkScopeContext,
globalContext
]
// 在调用checkScope函数时,不会立即执行,但是会进行准备工作,VO, scope chain, this等
checkScopeContext = {
Scope: [AO, ...[checkScope['[[scope]]']]],
AO: {
arguments: {
length: 0
},
scope2: undefined
}
}
// 进入调用
checkScopeContext = {
Scope: [AO, ...[checkScope['[[scope]]']]],
AO: {
arguments: {
length: 0
},
scope2: "local scope"
}
}
ECS = []
闭包(closure)
闭包是一种现象,内部函数,可以使用外部环境的变量。