1. 什么是作用域
任何编程语言都有作用域的概念,简单来说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。js的作用域是靠函数来形成的,也就是说一个函数的变量在函数外不可以访问。
技巧顺口溜:
全局带var要提升
声明提升在最前
全局无var必赋值
无var赋值当全局
局部全局要区分
局部声明在局部
永远不可出局部
局部分清明暗var
局部无var是全局
局部有var是局部
全局无法访局部
赋值位置很重要
结果由它来决定
变量函数名相同
变量提升就消失
顺口溜,实际例子
1、全局带var要提升,声明提升在最前
console.log(a); // undefined
var a = 20;
console.log(a); // 20
变量提升后的代码:
<script>
var a ;
console.log(a);
a = 20;
console.log(a);
</script>
分析:
(1) var 命名的全局变量会将其声明提升到全局作用域的最前面,可以假设将其提升至所有js代码的最前面
(2)只提升声明【var 变量名】,不提升变量赋值的位置
2、全局无var必赋值
a;
console.log(a); // Uncaught ReferenceError: a is not defined
b = 10;
console.log(b); // 10
分析
1、 a 由于没有var关键字且没有赋值,则报错
2、b没有var关键字但赋值了可以正常使用
3、无var赋值当全局
a = 1;
console.log(a == window.a); // true
分析:
因为window属性可以省略不写所以由上方的判等可以看出 a 属性全局变量
4、局部全局要区分
var a = 1; // 第一行
function fun(){
var a = 10; // 第三行
}
console.log(a); // 1
分析
(1)第一行的 var a 没在函数体内所以是全局变量
(2)第三行的 var a 由于在函数体内是局部变量
(3)由于作用域的问题全局不可以访问局部变量所以输出的是全局的a
5、局部声明在局部,永远不可出局部,局部分清明暗var
var a = 1;
var b = 2;
function fun(a){
a = 10;
var b = 20;
}
console.log(a); // 1
console.log(b); // 2
变量提升后的代码
var a = 1;
var b = 2;
function fun(a){
var a;
var b;
a = 10;
b = 20;
}
console.log(a); // 1
console.log(b); // 2
分析:
(1)由于函数有形参a可以假设在函数内部声明了一个var a 【暗var】所以函数中的 a = 10 看似是全局变量其实是局部变量
(2)局部变量的声明提升只能在局部中的最顶层且不能出局部作用域
(3)函数的形参的优先级高于函数内部声明的变量名的提升
6、局部无var是全局,局部有var是局部
var a = 1;
var b = 2;
function fun(a){
a = 10;
b = 20;
}
console.log(a); // 1
console.log(b); // 20
分析:
由于有形参 a 所以就在函数内部声明了一个 var a
由于函数内部没有声明 b 且 无形参 b ,所以局部的 b 就变成的全局变量 b
7、赋值位置很重要,结果由它来决定
var b = 2;
function fun(a){
b = 20;
}
var b = 23;
console.log(b); // 23
提升后的代码:
var b;
b = 2;
function fun(a){
b = 20;
}
b = 23;
console.log(b); // 23
分析:
虽然说变量名提升了,但是变量最后的结果却是由赋值的位置所决定的
8、变量函数名相同, 函数高于同变量
var fn = 10;
function fun(a){
b = 20;
console.log(b);
}
fn(); // Uncaught TypeError: fn is not a function
变量提升后的代码
var fn = function fun(a){
b = 20;
console.log(b);
}
var fn;
fn = 10;
fn(); // Uncaught TypeError: fn is not a function
分析:
由于函数名和变量名相同,带函数名的函数会整体提升(包括函数体)且高于变量名的提升,所以最后将函数赋值为了 10 再次调用时实际上是在 10(); 所以报错位不是函数。
9、变量名赋值的函数,会将变量名提升但函数不可提升
a(); // Uncaught TypeError: a is not a function
var a = function(){
var a = 1;
console.log(a);
}
a(); // 1
提升后的代码:
var a;
a(); // Uncaught TypeError: a is not a function
a = function(){
var a = 1;
console.log(a);
}
a(); // 1
10、立即执行函数【自调用函数】不会提升
var a = 2;
(function(){
a = 1;
console.log(a);
})();
11、作用域链
在函数中,用var声明的变量是函数的局部变量,如果访问的变量没有带var会去外界(上一层作用域)寻找带var
声明的变量,如果有就访问,如果没有会去上上层作用域继续去寻找,直到找到全局作用域,如果全局也没有用var定义的变量,就报错了,寻找的过程
形成的链条就是作用域链。
a = 0;
function fn1(){
function fn2(){
function fn3(){
function fn4(){
console.log(a); // 0
}
fn4();
}
fn3();
}
fn2();
}
fn1();
12、同时赋值和同时进行赋值
- 同时赋值
var a = 1;
var b = 2;
var c = 3;
function fn(){
var a,b,c;
a = 10;
b = 20;
c = 30;
}
fn();
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
var a , b , c; 带逗号的赋值为同时赋值三个变量,所以函数体内的a b c 都变成了局部变量,外部无法访问
2、同时进行赋值
var a = 1;
var b = 2;
var c = 3;
function fn(){
var a = b = c = 10;
}
fn();
console.log(a); // 1
console.log(b); // 10
console.log(c); // 10
var a = b = c = 10 等价于 : var a = 10; b = 10; c = 10;
由于 b , c 为 函数体内未带var所以为全局变量,所以改变了全局的 b c 值