一、什么是作用域
作用是对代码的一个读写操作,域是js能够实现的一个范围。
在script
标签中var
的时候,其实是在最大的window上加了一个属性,如果在script
中没有用var
声明,而是直接赋值,那就是自动在window上加一个属性,这是系统在找不到声明的时候,自动加的。
console.log(a); //undefined
var a = 1;
console.log(a); //1
console.log(a); //报错
a = 1;
a = 1;
console.log(window.a);//1
二、解释器
- js是一种直译式脚本遇到,遇到
script
标签后,解释器就开始工作,没有一个长期的前期准备。 - 解释器执行js代码至少分两步:预解析和执行代码。
- 预解析阶段进行变量声明和函数声明,遇到
var
变量声明后,不去读取赋值,而是自动给变量赋值为undefined,遇到function
函数声明后,不去读/执行函数,而是自动将函数的内容当成一块直接存起来function(){}。function
比var
优先级高,遇到同名的变量名和函数名,函数名会将变量名进行覆盖,而变量名不会将函数名覆盖。如果有重名的函数,则后者会将前者覆盖。 - 解释代码/执行代码期间才真正改变变量的值,先找到预解析找到的变量和函数,如果有能改变变量值的运算
=
、+
、*
、/
、%
、++
、--
,则会修改预先的值。
alert(a); //function a(){alert(3)}
var a = 1;
alert(a); //1
function a(){
alert(2);
}
alert(a); //1
var a = 3;
alert(a); //3
function a(){
alert(3);
}
alert(a) //3
a(); //报错
三、全局作用域和局部作用域
- 全局作用域:最大的作用域是
script
标签之间,全局中的最大对象是window,但是script
标签是分别解释的,代码是自上而下执行的,script
之后还是最大的window,在引入代码的时候,一定要注意引入顺序,因为script
标签是分别解释的。 - 局部作用域:函数内部也是一个空间(函数构建的空间),可以被看做一个局部作用域。
- 如果局部作用域的“预解析空间”(AO-活动对象(Active object))没有找到,那么代码会从上一级的作用域寻找,上级作用域不能在下级作用域寻找。
<script type="text/javascript">
alert(a); //报错
</script>
<script type="text/javascript">
var a = 1;
</script>
<script type="text/javascript">
var a = 1;
</script>
<script type="text/javascript">
alert(a); //1
</script>
var a = 1;
function fn1(){
alert(a); //undefined
var a = 1;
}
fn1();
alert(a); //1
//window 1.Ao分析 {a:undefined,fn1:function(){}}
//
// 2. 代码执行: a = 1
//
//
// fn1 执行 : 进入到函数内部/函数所生成的局部作用域
// 1. AO分析 {a:undefined}
// 2.执行
// //
//
//
// alert(a)
//
//
四、参数
- 形参:相当于隐式声明,在AO上为undefined。
- 实参:会在AO分析的时候,给形参一个初始值。
var a = 1;
function fn1(a){
alert(a); //undefined
a = 2;
}
fn1();
alert(a); //1
// window 1. AO {a:undefined,fn1:function(){}}
// 2. AO {a:1,fn1:funcion(){}}
// fn1:
// 1.AO {a:undefined}
// 2.alert(a);
// AO {a:2}
//
//
// alert(a)
//
var a = 1;
function fn1(a){
alert(a); //1
a = 2;
}
fn1(a);
alert(a); //1
// window 1.{a:un,fn1:function}
// 2.{a:1,fn1:function}
// fn1: 1.{a:1}
// 2. alert(1)
// {a:2}
// alert(a)
五、作用域的AO分析
- 先看有没有参数,实参会把形参初始化,如果只有形参,那么形参为undefined。
- 再看有没有变量声明。
- 最后看有没有函数声明,函数会把同名变量覆盖。
var age = 99;
function t(age){
alert(age);
}
t(5); //5
t(); //undefined
// win 1.AO{age:un,t:fun}
// 2. AO{age:99}
// t(5): 1. AO{age:5}
// 2. alert(5)
// t(): 1 AO{age:un}
// 2. alert(un)
function t2(greet){
alert(greet); //function greet(){}
function greet(){}
greet = 'hello';
alert(greet); //hello
}
t2(null);
// t2 1. AO{greet:function(){}}
// 2. alert(greet)
// AO{greet:"hello"}
// alert(greet)
function a(b){
alert(b); //function b(){alert(b);}
function b(){
alert(b); //function b(){alert(b);}
}
b();
}
a(1);
// a 1. AO{b:fun}
// 2. alert(b)
// b() 1. b AO{}
// 2. alert(b)
function a(b){
alert(b); //1
b = function(){
alert(b); //function(){alert(b);}
}
b();
}
a(1);
// a 1. AO{b:1}
// 2. alert(b)
// {b:fun}
六、作用域链
- 内层作用域在寻找变量的时候未找到,会沿着作用域上的AO向上寻找,直到全局。
- AO成链就是作用域链。
//JQ最外层代码
// console.log(window);
// undefined = 1
;(function(window ,undefined) {
// AO{window:window,undefined:1}
function(){
function(){
function(){
window.document.getElementById()
}
}
}
})(window);
//写window 是为了 性能 不写undefined 是为了防止被污染
//在低版本的IE和FF浏览器当中 undefined是可以被赋值的