JS代码的执行过程分为两个阶段:
1.预解析过程:找到所有的变量和函数声明,提升到作用域的最前面
2.执行过程
1.作用域
变量起作用的范围就是变量的作用域。在JavaScript中唯一能产生作用域的东西是函数。
1)块级作用域:使用代码块限定的作用域。JavaScript中没有块级作用域
2)词法作用域:在代码写好的那一刻,变量的作用域就已经确定了。和词法作用域相对的是动态作用域,JS中没有
使用规则:
- 函数允许访问函数外的变量
- 整个代码结构中只有函数可以限定作用域
- 作用域规则首先使用提升规则分析
- 如果当前作用域中有了该变量,就不再考虑外面的同名变量
2.关于变量声明
- 设置值的时候,也是访问变量;获取值的时候也是访问变量
- 并不是在函数内部写了变量,这个变量就属于这个函数的作用域;而是必须使用
var
来声明变量,这个变量才会属于这个作用域 - 函数在声明的时候里面的代码不会执行,只有在调用的时候,代码才会执行
- 声明函数时的函数名,其实也是一个变量名,可以通过这个变量名来给其赋值
3.用var、let、const 声明变量时的区别
1)用 var 声明的变量作用域是它当前的执行上下文,它可以是嵌套的函数,也可以是声明在任何函数外的变量。而 let 和 const 是块级作用域,意味着他们只能在最近的一组花括号(function、if-else 代码块或 for 循环)中访问。
if(true){
var bar = "bar";
let baz = "baz";
const qux = "qux";
}
console.log(bar);//bar
console.log(baz);//baz is not defined
console.log(qux);//qux is not defined
2)var 会使变量提升,这意味着变量可以在声明之前使用。而 let 和 const 不会使变量提升,提前使用会报错。
console.log(foo);//undefined
//这里注意var声明的变量可以提升但是赋值不可以,所以是undefined
var foo = "foo";
console.log(baz);//ReferenceError
let baz = "baz";
console.log(bar);//ReferenceError
const bar = "bar";
3)用 var 重复声明变量,不会报错,但是 let 和 const 这样做会报错。
var foo = "foo";
var foo = "bar";
console.log(foo); //bar
let baz = "baz";
let baz = "qux";//SyntaxError
4)let 和 const 的区别在于:let 允许多次赋值,而 const 只允许一次。
//不会报错
let foo = "foo";
foo = "bar";
//会报错
const baz = "baz";
baz = "qux";
4.变量提升和函数提升
在分析代码的时候,首先应将以var
声明变量名和以function
开头的函数进行提升,再去执行代码的具体执行过程。
1.变量的提升是分作用域的
function foo(){
var num = 123;
console.log(num);
}
foo();//123
console.log(num);//num is not defined
这里需要注意的是:
undefined 是定义了没有赋值
is not defined 是没有定义
2.函数表达式中函数的声明不会被提升,但是变量会被提升
var scope = "global";
foo();
function foo(){
console.log(scope);//undefined
var scope = "local";
console.log(scope);//local
}
//提升后的代码为
var scope;
function foo(){
var scope;
console.log(scope);//undefoned
scope = "loacl";
console.log(scope);//local
}
foo();
scope = "global";
3.函数同名:预处理的时候,会将两个函数全部提升,但是后面的函数会将前面的函数覆盖
var m = 1;
function add(n){
return n = n + 1;
}
y = add(m);
function add(n){
return n = n + 3;
}
z = add(m);
console.log(y);//4
console.log(z);//4
function add() { }
定义的函数会优先解析,而不是顺序解析。因此整个过程中,首先解析两个 add 函数,但是由于两个函数同名,所以后者会覆盖前者;然后顺序解析其余的代码。y = add(m);
和 z = add(m);
语句调用的都是第二个 add 函数,所以返回的值都是4。
4.变量与函数同名:在提升的时候,如果有变量和函数同名,则会忽略掉变量,只提升函数
console.log(foo); //foo(){ }
function foo(){}
var foo = 2;
console.log(foo); //2
//提升后的代码
function foo(){};
alert(foo);
foo=2;
alert(foo);
5.变量提升只会将变量和函数提升到当前作用域的最上方
//例1
if(a in window){
var a = 10;
}
console.log(a);//10
//提升之后的代码
var a;
if(a in window){
a = 10;
}
cosnole.log(a);//10
//例2
function f1(){
if(a in window){
var a = 10;
}
console.log(a);
}
f1();//undefined
//提升后的代码
function f1(){
var a;
if(a in window){
a = 10;
}
console.log(a);
}
f1();//undefined