预编译
预编译前奏
- 函数声明整体提升
- 变量 声明提升(只声明提升,直接赋值不提升)
函数预编译
- 创建A0对象 Activation Object
- 找形参和变量声明,将形参和变量声明作为AO属性名,值为undefined
- 将实参值和形参统一
- 在函数体内找函数声明(把函数声明的名作为属性值加入AO),值赋予函数体
- 执行
function fn(a){
console.log(a);
var a=123;
console.log(a);
function a(){}
console.log(a);
var b=function(){}
console.log(b);
function d(){}
}
fn(1);
//执行过程
1.创建AO
A0{
}
2.找实参和变量声明,作为AO属性名,值为undefined
AO{
a:undefined,
b:undefined
}
3.将实参形参统一
AO{
a:1,
b:undefind
}
4.找函数声明,将值赋予函数体
AO{
a:1,
b:undefined,
d:
}
AO{
a:function a(){},
b:undefined,
d:function(){}
}
5.执行
打印 function a(){}
a=123
打印 123
打印 123
b=function(){}
打印 function(){}
全局预编译
+ 1.创建G0对象 Global Object (window)
+ 2.找形参和变量声明,将形参和变量声明作为AO属性名,值为undefined
+ 3.找函数声明(把函数声明的名作为属性值加入AO),值赋予函数体
+ 4.执行
预编译练习题
1.
function bar() {
return foo;
foo = 10;
function foo() {}
var foo = 11;
}
console.log(bar());
2.
console.log(bar());
function bar() {
foo = 10;
function foo() {}
var foo = 11;
return foo;
}
作用域&作用域链
执行期上下文
当函数执行的前一刻,会创建一个称为执行期上下文的内部对象(就是前面说的的AO)。
一个执行期上下文定义了一个函数执行时的环境。
函数每次执行时对应的执行期上下文都是独一无二的。
所以多次调用一个函数会导致创建多个执行期上下文。
当函数执行完毕,他创建的执行期上下文被销毁。
解释:我们每次执行函数的前一刻系统都会创建 AO 对象,
这个 AO 就定义了变量提升 函数提升等,
比如说定义了一个函数 test,当你调用他的时候他会创建一个 AO,
当你再次调用的时候他还会再次创建另一个 AO,
AO只是一个临时的存储对象,当函数执行完毕时,AO 就会被销毁。
[[scope]] -->作用域
每个JavaScript函数都是一个对象,对象中有些属性我们可以访 问,但有些不可以,这些属性仅供 JavaScript 引擎存取,[[scope]]就是其中一个, [[scope]]指的就是我们所说的作用域,其中存储了运行期上下文的集合。
作用域链
[[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式链 接,我们把这种链式链接叫做作用域链。在哪个函数里边查找变量,就去哪个函数的 作用域链顶端依次向下查找
function a(){
function b(){
function c(){
}
c();
}
b();
}
a();
解析:
a 函数被定义:a. [[scope]]------->0:GO
a 函数执行前:a. [[scope]]------->0:a 的 AO
1:GO
b 函数被定义:b. [[scope]]------->0:a 的 AO
1:GO
b 函数执行前:b. [[scope]]------->0:b 的 AO
1:a 的 AO
2:GO
c 函数被定义:c. [[scope]]------->0:b 的 AO
1:a 的 AO
2:GO
c 函数执行前:c. [[scope]]------->0:c 的 AO
1:b 的 AO
2:a 的 AO
3:GO
闭包的解决方法
在形成闭包的函数外面加一层立即执行函数。
function test(){
var arr = [];
for(var i = 0;i < 10;i ++){
<!--此处为解决方法-->
(function (j){
arr[j] = function (){
document.write(j + " ");
}
}(i))
}
return arr;
}
var myArr = test();
for(var j = 0;j < 10;j ++){
myArr[j]();
}
- 如果不加立即执行函数,本想打印出 0123456789,结果会打印十个10。
原理:循环里面的函数会在循环结束后执行,此时i的值已经变成了10. - 我们在 for 循环里加一个立即执行函数。
原理: 形参传一个 j 进去,实参传 i,把里边 的 i 换成 j 就完美解决了。
每次形成闭包所绑定的值j都是固定的,就这样绑定十次,自然不会出错。