1. 变量的作用域
全局变量
定义:在函数外声明的变量
作用范围:在整个JS文档中生效
生命周期:页面关闭时销毁
局部变量
定义:在函数内用
var
声明的变量作用范围:只能在函数内部使用
生命周期:函数执行完毕时销毁
作用域规则
1.1 全局变局部
全局变量可以在函数内部的任意位置使用
1.2 局部变全局
局部变量不能在全局使用
两种方法在全局使用局部变量:
去除
var
关键字(隐式全局变量,不推荐)使用
return
返回变量值
块级作用域(ES6新增)
let
和const
声明的变量具有块级作用域只在声明它们的
{}
内有效
2. 作用域链
概念
函数中如果在当前作用域中没有查到值,就会向上级作用域去查
直到查到全局作用域,这样形成的链条就叫做作用域链
特点
内部环境可以通过作用域链访问所有外部环境
外部环境不能访问内部环境的任何变量和函数
示例分析
var n = 10;
function outer(){function inner(){function center(){console.log(n); // 输出 undefined}center();}inner();var n = 15; // 变量提升,但赋值在console之后
}
outer();
3. 预解析(变量提升)
执行过程
预解析阶段:提前加载所有
var
和function
代码执行阶段:按书写顺序执行
预解析规则
var
:只声明不定义(值为undefined
)function
:既声明又定义(包含函数体)函数优先级高于变量
预解析只发生在当前作用域下
优先级顺序
函数声明(最高优先级)
变量声明
代码执行
4. 案例分析
案例1
var num = 10;
fun();
function fun(){console.log(num); // undefinedvar num = 20;
}
案例2
var num = 10;
function fun() {console.log(num); // undefinedvar num = 20;console.log(num); // 20
}
fun();
案例3
var a = 18;
f1();
function f1() {var b = 9;console.log(a); // undefinedconsole.log(b); // 9var a = 13;
}
案例4
console.log(a, b, c); // undefined undefined undefined
var a = b = c = 5; // b和c成为隐式全局变量
案例5
f1();
console.log(c); // 9
console.log(b); // 9
console.log(a); // 报错
function f1() {var a = b = c = 9; // 只有a是局部变量console.log(a); // 9console.log(b); // 9console.log(c); // 9
}
案例6
fn(); // 报错
var fn = function(){console.log(11);
}
案例7
function fn(){console.log(a); // 报错a = 100;
}
fn();
console.log(a);
案例8
console.log(show); // function show
function show(){console.log(123);
}
var show = 10;
案例9
function show(){console.log(123);
}
var show = 10;
console.log(show); // 10
5. 重要概念总结
变量提升等价代码
// 原始代码
console.log(a);
var a = 'hello';
function a(){console.log(123);
}// 提升后等价代码
function a(){console.log(123);
}
// var a; // 被忽略
console.log(a); // function a
a = 'hello';
作用域链案例
var a = 1
function fn1(){function fn2(){console.log(a) // 输出2}function fn3(){var a = 4fn2()}var a = 2return fn3
}
var fn = fn1()
fn()
6. 最佳实践
使用
let
/const
代替var
避免隐式全局变量
函数优先使用函数表达式
启用严格模式
"use strict"
变量声明放在作用域顶部