JavaScript基础
- 作用域
- 思考
- 执行上下文
- 顺序执行
- 可执行代码
- 执行上下文栈
- 案例一
- 案例二
- case1:
- case2
作用域
- 作用域:程序源代码中定义变量的区域。
- 作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。
- 作用域分类:静态作用域(词法作用域)和动态作用域
JavaScript是静态作用域 - 静态作用域:函数的作用域在
函数定义
的时候就决定了 - 动态作用域:函数的作用域是在
函数调用
的时候才决定的
var value = 1;function foo() {console.log(value);
}function bar() {var value = 2;foo();
}bar();// 结果是 ???
JavaScript采用静态作用域,于是执行 foo 函数,先从 foo 函数内部查找是否有局部变量 value,如果没有,就根据书写的位置,查找上面一层的代码,也就是 value 等于 1,所以结果会打印 1。
如果是动态作用域的话,于是执行 foo 函数,依然是从 foo 函数内部查找是否有局部变量 value。如果没有,就从调用函数的作用域,也就是 bar 函数内部查找 value 变量,所以结果会打印 2。
思考
// 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
结果都是
local scope
JavaScript采用的是词法作用域,函数的作用域基于函数创建的位置。
执行上下文
顺序执行
js是从上到下执行的
- 案例一
var foo = function () {console.log('foo1');}foo(); // foo1var foo = function () {console.log('foo2');}foo(); // foo2
但是
- 案例二
function foo() {console.log('foo1');}foo(); // foo2function foo() {console.log('foo2');}foo(); // foo2
以上代码等同于:
function foo() {console.log('foo1');
}
function foo() {console.log('foo2');
}
foo(); // foo2
foo(); // foo2
- 案例三
console.log(add2(1,1)); //输出2
function add2(a,b){return a+b;
}
等同于:
function add2(a,b){return a+b;
}
console.log(add2(1,1)); //输出2
- 案例四
console.log(add1(1,1)); //报错:add1 is not a function
var add1 = function(a,b){return a+b;
}// 用函数语句创建的函数add2,函数名称和函数体均被提前,在声明它之前就使用它。
// 但是使用var表达式定义函数add1,只有变量声明提前了,变量初始化代码仍然在原来的位置,没法提前执行。
因为 JavaScript 引擎并非一行一行地分析和执行程序,而是一段一段地分析执行。当执行一段代码的时候,会进行一个“准备工作”。
用函数语句创建的函数add2,函数名称和函数体均被提前,在声明它之前就使用它。
但是使用var表达式定义函数add1,只有变量声明提前了,变量初始化代码仍然在原来的位置,没法提前执行。
可执行代码
可执行代码类型:
- 全局代码
- 函数代码
- eval代码
例如,代码执行到函数时候,就会做一些准备工作,就叫执行上下文
执行上下文 ->准备工作,准备去执行
执行上下文栈
JavaScript 引擎创建了执行上下文栈(Execution context stack,ECS)来管理执行上下文的执行顺序
栈=》FILO 先进后出
模拟执行上下文栈的行为,将执行上下文栈定义为一个数组:
ECStack = [];
案例一
例如以下代码的执行过程:
function fun3() {console.log('fun3')
}function fun2() {fun3();
}function fun1() {fun2();
}fun1();
初始化:
ECStack = [];
ECStack = [globalContext,//全局执行上下文
];
// 伪代码// fun1()
ECStack.push(<fun1> functionContext);// fun1中竟然调用了fun2,还要创建fun2的执行上下文
ECStack.push(<fun2> functionContext);// 擦,fun2还调用了fun3!
ECStack.push(<fun3> functionContext);// fun3执行完毕
ECStack.pop();// fun2执行完毕
ECStack.pop();// fun1执行完毕
ECStack.pop();// javascript接着执行下面的代码,但是ECStack底层永远有个globalContext
案例二
// case 1
var scope = "global scope";
function checkscope(){var scope = "local scope";function f(){return scope;}return f();
}
checkscope();// case 2
var scope = "global scope";
function checkscope(){var scope = "local scope";function f(){return scope;}return f;
}
checkscope()();
case1:
ECStack.push(<checkscope> functionContext);
ECStack.push(<f> functionContext);
ECStack.pop();
ECStack.pop();
case2
ECStack.push(<checkscope> functionContext);
ECStack.pop();
ECStack.push(<f> functionContext);
ECStack.pop();