作用域
- JavaScript中作用域的概念和C语言一样, 也分为全局作用域和局部作用域
- 全局作用域
- 在任何地方都可以访问到的就是全局作用域
- 在script标签中或一个独立js文件中定义的就是全局作用域
<script>
var num = 10; // 该变量是全局作用域
function test() {
// 在函数中访问全局作用域num变量
console.log("test中"+num);
}
test();
// 在外面访问全局作用域num变量
console.log("外部"+num);
</script>
function test() { // 该函数是全局作用域
console.log("test函数");
}
// 在外面访问全局作用域test函数
test();
function demo() {
// 在函数中访问全局作用域test函数
test();
}
demo();
- 局部作用域
- 只在固定的代码片段内可访问到的变量,例如函数内部。对应局部作用域(函数作用域)
- 写在函数内部的都是局部作用域, 只能在当前函数内访问
<script>
function test() {
var num = 123; // 该变量是局部作用域
// 可以在同一个局部作用域中访问
console.log("test中"+ num);
}
// 不能在局部范围以外访问
console.log("外部"+num);
</script>
<script>
function test() {
function demo() { // 该函数是局部作用域
console.log("demo函数");
}
// 可以在同一个局部作用域中访问
demo();
}
// 不能在局部范围以外访问
demo();
</script>
- 注意点:
- 如果在函数中定义变量时,如果不添加var关键字, 这个变量是一个全局变量
<script>
function test() {
// 企业开发千万不要这么写
num = 123;
console.log(num);
}
test();
console.log(num);
</script>
- 任何一对花括号({和})中的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域。
- 在es5之前没有块级作用域的的概念,只有函数作用域,现阶段可以认为JavaScript没有块级作用域
作用域链
- JavaScript代码中至少有一个作用域, 即全局作用域。
- 凡是代码中有函数,那么这个函数就构成另一个作用域。
- 如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域。
- 将这样的所有的作用域列出来,可以形成的结构就称之为作用域链。
<script>
var num = 123; // 0级作用域链
function test() { // 0级作用域链
var num = 666; // 1级作用域链
console.log(num);
function demo() { // 2级作用域链
var num = 777;
console.log(num);
}
demo();
}
test();
console.log(num);
</script>
再直白一点: 访问时就是C语言中的就近原则
预解析
- JavaScript代码的执行是由浏览器中的JavaScript解析器来执行的。
- JavaScript解析器执行JavaScript代码的时候,分为两个过程:
- 预解析过程
- 代码执行过程
- 预解析过程:
- 把变量的声明提升到当前作用域的最前面,只会提升声明,不会提升赋值。
- 把函数的声明提升到当前作用域的最前面,只会提升声明,不会提升调用。
- 先提升var,在提升function。
- 案例1
<script>
console.log(num); // undefined
var num = 123;
</script>
<script>
// 把变量的声明提升到当前作用域的最前面
var num;
console.log(num);
num = 123;
</script>
- 案例2:
<script>
test();
function test() {
console.log("hello world");
}
</script>
<script>
test();
alert(test);
function test() {
console.log("hello world");
}
</script>
<script>
// 把函数的声明提升到当前作用域的最前面
function test() {
console.log("hello world");
}
test();
alert(test);
</script>
- 预解析练习
var num = 123;
fun();
function fun() {
console.log(num);
var num = 666;
}
/*
var num;
function fun() {
var num;
console.log(num);
num = 666;
}
num = 123;
fun();
*/
var a = 666;
test();
function test() {
var b = 777;
console.log(a);
console.log(b);
var a = 888;
}
/*
var a;
function test() {
var b;
var a;
b = 777;
console.log(a);
console.log(b);
a = 888;
}
a = 666;
test();
*/
- 注意点:
- 变量和函数同名时, 函数的优先级高
<script>
console.log(num);
function num() {
console.log("hello world");
}
var num = 666;
console.log(num);
/*
function num() {
console.log("hello world");
}
var num;
console.log(num);
num = 666;
console.log(num);
*/
</script>
不同方式定义函数区别
- 预解析时提升的方式不同
<script>
// 1.函数声明
test();
function test() {
console.log("指趣学院");
}
// 2.函数表达式
demo(); // 报错
var demo = function () {
console.log("www.it666.com");
}
/*
function test() {
console.log("指趣学院");
}
var demo;
test();
demo();
demo = function () {
console.log("www.it666.com");
}
*/
</script>
- 新旧版本浏览器预解析处理方式不同
<script>
/*
在新版本浏览器中, 代码块中的函数不会被提升
在旧版本浏览器中, 代码块中的函数会被提升
新版本输出"指趣学院"
老版本输出"www.it666.com"
*/
if(true){
function test() {
console.log("指趣学院");
}
}else{
function test() {
console.log("www.it666.com");
}
}
test();
</script>
<script>
var test;
if(true){
test = function () {
console.log("指趣学院");
}
}else{
test = function() {
console.log("www.it666.com");
}
}
test();
</script>