预解析机制
作用域
域:空间、范围、区域……
作用:读、写
script 全局变量、全局函数,自上而下
函数 由里到外
浏览器:“JS解析器”
1)“找一些东西” :var function 参数
a = …
所有的变量,在正式运行代码之前,都提前赋了一个值:未定义
fn1 = function fn1(){ alert(2); }
所有的函数,在正式运行代码之前,都是整个函数块
JS 的预解析
遇到重名的:只留一个
变量和函数重名了,就只留下函数
2)逐行解读代码:
表达式:= + - * / % ++ – ! 参数……
表达式可以修改预解析的值!
小例子
1 | alert(a); // 输出 function a (){ alert(4); } |
js单线程运行
1 | <script> |
前一个script区域运行完之后,值会保存。
1 | <script> |
函数也是一个局部的域
函数调用:预解析+逐行解读代码
1)预解析: var function 参数….
a=…
fn1=function(){
alert(a);
var a=2;
}
2)逐行解读代码:
表达式
函数调用
2.1)预解析:
a=… 这里的a是局部变量
2.2)逐行解读代码:
a=2;
1 | var a = 1; |
下面看看 不加var的影响
1)预解析: var function 参数….
a=…
fn1=function(){
alert(a);
a=2;
}
2)逐行解读代码:
表达式
函数调用
2.1)局部预解析:
因为函数里面没有var 所有没有找到,
2.2)局部逐行解读代码:
a=2;
1 | var a = 1; |
小结:先找东西,后执行。执行过程中遇到表达式就会去修改原来库里面的东西,如果遇到函数调用就开了一个新的作用域,一旦新的作用域开始了,又会重复以上步骤,新的作用域中如果没找到里面有东西,又会返回父级里面去找。
带参数的函数
1)预解析: var function 参数….
a=…
fn1=function(a){
alert(a);
a=2;
}
2)逐行解读代码:
表达式
函数调用
2.1)局部预解析:
因为函数里面没有var ,也没有找到函数,所有没有找到。参数本质上就是一个局部变量。找到了参数,但是这个例子里函数调用并没有传进来。
参数 a=…未定义
2.2)局部逐行解读代码:
局部变量a就是undefined
1 | var a = 1; //全局变量a=1 |
函数调用带参数
1)预解析: var function 参数….
a=…
fn1=function(a){
alert(a);
a=2;
}
2)逐行解读代码:
表达式
函数调用fn1(a)
2.1)局部预解析:
参数本质上就是一个局部变量,因为这个例子里面传递了参数进来,所以a=undefined。
参数 a=…未定义
2.2)局部逐行解读代码:
因为传递进来的a=1,所以局部变量a=1,再下一步就是局部变量a=2。 全局变量a仍然是1
1 | var a = 1; |
任何函数都可以改全局变量
因为函数内部没有这个变量的话,会从里往外找。
1 | var num = 0; |
获取函数内的值
巧妙利用全局变量来去获取函数内的内容
1 | var str = ''; |
另一种方法:
1 | function fn2(){ |
注意事项
if(){}和for(){}、do{}while不是作用域,函数是作用域。
firefox不能对下面的函数进行预解析,除了火狐以外,其他的浏览器都可以。
1 | alert( fn1 ); // FF 不能对下面的函数进行预解析 |
解决这个兼容性问题的方法是:以后尽量不要在if语句里面定义函数,全局变量。改成下面这种写法:
for里面包了一个函数,在函数里面不要直接使用i
下例会出现undefined的原因是:点击事件函数是一个作用域,只要有作用域,就会有域解析,点击事件函数里面域解析的时候会找到var i=undefined,所以这里alert(i)是undefined。但是如果把for循环里面的var去掉的话,那么点击事件函数里面就没有变量,所以i就会到父级里面去找,父级i是3,所以此时alert(i)会弹出3。
1 |
|
但是如果把js代码改成以下就会出错
1 | <script> |