undefined

js作用域

预解析机制

作用域

域:空间、范围、区域……
作用:读、写
script 全局变量、全局函数,自上而下
函数 由里到外
浏览器:“JS解析器”
1)“找一些东西” :var function 参数
a = …
所有的变量,在正式运行代码之前,都提前赋了一个值:未定义
fn1 = function fn1(){ alert(2); }
所有的函数,在正式运行代码之前,都是整个函数块
JS 的预解析
遇到重名的:只留一个
变量和函数重名了,就只留下函数

2)逐行解读代码:
表达式:= + - * / % ++ – ! 参数……
表达式可以修改预解析的值!

小例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    alert(a);					// 输出   function a (){ alert(4); }
//因为预解析之后,变量和函数重名了就只留下函数,所以a是整个函数块 function a(){alert(4)};
var a = 1;
alert(a); // 输出 1
//表达式var a=1;修改了预解析的值
function a (){ alert(2); }
alert(a); // 输出 1
// 函数不修改值
var a = 3;
alert(a); // 输出 3
//表达式var a=3;修改了预解析的值
function a (){ alert(4); }
alert(a); // 输出 3
//函数不修改值
alert( typeof a ); //number
// a(); // 报错

js单线程运行

1
2
3
4
5
6
7
8
<script>
alert(a); //报错
</script>

<script>
var a=1; //1
alert(a);
</script>

前一个script区域运行完之后,值会保存。

1
2
3
4
5
6
7
8
<script>
var a=1; //1
alert(a);
</script>

<script>
alert(a); //1
</script>

函数也是一个局部的域

函数调用:预解析+逐行解读代码
1)预解析: var function 参数….
a=…
fn1=function(){
alert(a);
var a=2;
}
2)逐行解读代码:
表达式
函数调用
2.1)预解析:
a=… 这里的a是局部变量
2.2)逐行解读代码:
a=2;

1
2
3
4
5
6
7
var a = 1;
function fn1(){
alert(a); // undefined
var a = 2; //a=2
}
fn1();
alert(a); // 1

下面看看 不加var的影响
1)预解析: var function 参数….
a=…
fn1=function(){
alert(a);
a=2;
}
2)逐行解读代码:
表达式
函数调用
2.1)局部预解析:
因为函数里面没有var 所有没有找到,
2.2)局部逐行解读代码:
a=2;

1
2
3
4
5
6
7
var a = 1;
function fn1(){
alert(a); // 1 所以返回父级去找。由里到外(作用域链)找到外面那个a
a = 2; //这里修改的是外面的那个a
}
fn1();
alert(a); // 2

小结:先找东西,后执行。执行过程中遇到表达式就会去修改原来库里面的东西,如果遇到函数调用就开了一个新的作用域,一旦新的作用域开始了,又会重复以上步骤,新的作用域中如果没找到里面有东西,又会返回父级里面去找。

带参数的函数

1)预解析: var function 参数….
a=…
fn1=function(a){
alert(a);
a=2;
}
2)逐行解读代码:
表达式
函数调用
2.1)局部预解析:
因为函数里面没有var ,也没有找到函数,所有没有找到。参数本质上就是一个局部变量。找到了参数,但是这个例子里函数调用并没有传进来。
参数 a=…未定义
2.2)局部逐行解读代码:
局部变量a就是undefined

1
2
3
4
5
6
7
var a = 1;                           //全局变量a=1
function fn1(a){
alert(a); // undefined
a = 2; //这句把a改成2
}
fn1();
alert(a); // 这里读取父级的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
2
3
4
5
6
7
var a = 1;
function fn1(a){
alert(a); // 1
a = 2;
}
fn1(a);
alert(a); // 1

任何函数都可以改全局变量

因为函数内部没有这个变量的话,会从里往外找。

1
2
3
4
5
6
7
8
9
10
11
12
var num = 0;
function fn1(){
num++;
}
function fn2(){
num--;
}

fn2();
fn1();
fn2();
alert(num);

获取函数内的值

巧妙利用全局变量来去获取函数内的内容

1
2
3
4
5
6
7
var str = '';
function fn1(){
var a = '大鸡腿~';
str = a;
}
fn1();
alert( str ); //大鸡腿

另一种方法:

1
2
3
4
5
6
7
8
9
function fn2(){
var a = '9999999克拉钻石23456789';
fn3(a);
}
fn2();

function fn3(a){
alert(a);
}

注意事项

if(){}和for(){}、do{}while不是作用域,函数是作用域。
firefox不能对下面的函数进行预解析,除了火狐以外,其他的浏览器都可以。

1
2
3
4
5
6
7
alert( fn1 );		// FF 不能对下面的函数进行预解析
if( true ){
var a = 1;
function fn1(){
alert(123);
}
}

解决这个兼容性问题的方法是:以后尽量不要在if语句里面定义函数,全局变量。改成下面这种写法:

for里面包了一个函数,在函数里面不要直接使用i

下例会出现undefined的原因是:点击事件函数是一个作用域,只要有作用域,就会有域解析,点击事件函数里面域解析的时候会找到var i=undefined,所以这里alert(i)是undefined。但是如果把for循环里面的var去掉的话,那么点击事件函数里面就没有变量,所以i就会到父级里面去找,父级i是3,所以此时alert(i)会弹出3。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!DOCTYPE HTML>
<html>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>无标题文档</title>
<script>
window.onload = function() {
var aBtn = document.getElementsByTagName('input');

for (var i = 0; i < aBtn.length; i++) {
aBtn[i].onclick = function() {

alert( i );

for (var i = 0; i < aBtn.length; i++) {
aBtn[i].style.background = 'yellow';
}
};
}
};
</script>
</head>

<body>
<input type="button" value="按钮1" />
<input type="button" value="按钮2" />
<input type="button" value="按钮3" />
</body>

</html>


但是如果把js代码改成以下就会出错

1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
window.onload = function() {
var aBtn = document.getElementsByTagName('input');

for (var i = 0; i < aBtn.length; i++) {
aBtn[i].onclick = function() {

alert( i ); // 3
aBtn[i].style.background = 'yellow';
};
}
};
</script>

觉得本站不错,请作者吃根辣条