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(); // foo1
var foo = function () {
console.log('foo2');
}
foo(); // foo2
但是
- 案例二
function foo() {
console.log('foo1');
}
foo(); // foo2
function 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();