I AM MAO.YOUR FRIEND.WELCOME TO HERE.

每一个不曾起舞的日子都是对生命的辜负

对作用域的一些理解

作用域中的域就是指空间、范围、区域……

如 < script >< /script >、函数和json数据都是域都会执行浏览器解析器的两个主要的步骤。

作用域的作用就是读和写。

< script >< /script > 全局变量、全局函数:自上而下

函数:可以由里到外得到变量,不能从外到里

浏览器解析:“JS解析器”两个主要步骤:

1)“找一些东西” :var声明、function函数、函数中的参数

初始化 a = undefined 。所有的变量,在正式运行代码之前,都提前赋了一个值:未定义。

fn1 = function fn1(){ alert(2); }。所有的函数,在正式运行代码之前,都是整个函数块

JS 的预解析:遇到重名的话,只留一个。如果变量和函数重名了,就只留下函数。

2)逐行解读代码:

表达式可以修改预解析的值!

常见的表达式:= + - * / % ++ – ! 函数中的参数…

遇到函数,函数也要进行预解析和代码解读,函数里的由上到下先找函数中变量的预解析值,找到变量的值就变为预解析值

在函数中如果预解析找不到var、function、参数,则沿着作用域链向父级寻找

例子一:

弹出的内容是什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
alert(a); // 弹出内容为:function a (){ alert(4); } //最先弹出的是预解析内容,该函数在预解析中和变量重名,因此只留下该函数块。
var a = 1;
alert(a); // 1
function a (){ alert(2); }
alert(a); // 1
var a = 3;
alert(a); // 3 //上一步表达式修改了a的值
function a (){ alert(4); }
alert(a); // 3
alert( typeof a ) ; //number
// a(); // 报错

例子二:

1
2
3
4
5
6
7
var a = 1;
function fn1(){ //函数也会执行预解析和代码解读两个步骤
alert(a); // undefined //函数中的a是局部的和外面的a不同,等于函数中的预解析值
var a = 2;
}
fn1();
alert(a); // 1 //< script >< /script >中的预解析值,即第一行代码的a的值

例子三:

1
2
3
4
5
6
7
var a = 1;
function fn1(){
alert(a); // 1 //函数中没有var、function、参数所以没有预解析值,则沿着作用域链向父级寻找
a = 2; // 把父级a的预解析值改成了2
}
fn1();
alert(a); // 2 //a已被函数改成了2

例子四:

1
2
3
4
5
6
7
var a = 1;
function fn1(a){
alert(a); // undefined //函数中存在参数,不会向父级寻找a,因为参数也会有预解析值,此时参数a = undefined
a = 2;
}
fn1();//调用时没加参数
alert(a); // 1

例子五:

1
2
3
4
5
6
7
8
var a = 1;
function fn1(a){ // 参数在预解析中 a = undefined ,
alert(a); // 1 //后面fn1(1)调用时参数为1.
a = 2; //此时a被修改成了2,但函数里的a和外面的a不是同一个a。所以最后一句alert出的还是第一行中a的值:1.
}
fn1(a);//调用时有参数 a=1
alert(a); // 1

例子六:

函数可以由里到外访问变量,不能由外到里。若函数外面想要获取函数内的值:

1)

1
2
3
4
5
6
7
var str = ''; //可以先定义一个空字符串
function fn1(){
var a = '啦啦';
str = a;
}
fn1();
alert( str ); //啦啦

2)

1
2
3
4
5
6
7
8
9
function fn2(){
var a = '9999999克拉钻石23456789';
fn3(a);//参数中的a就是上一行代码中a的值
}
fn2();
function fn3(a){
alert(a); //9999999克拉钻石23456789
}

例子七:

页面有三个按钮,在点击任意一个按钮的时候使三个按钮背景颜色变成黄色:

1)错误示范

1
2
3
4
5
6
7
8
9
10
var aBtn = document.getElementsByTagName('input');
for( var i=0; i<aBtn.length; i++ ){
aBtn[i].onclick = function (){
// alert( i );// 3。结果为3,因为for括号中的i++后i已经为3,此时函数中的i访问父级中的i就为3
//而aBtn[i]就为aBtn[3],因为长度为3,最后一个元素的索引值为2,因此访问不到,会报错
aBtn[i].style.background = 'yellow';//此时不仅不变颜色而且控制台会报错
};
}

2)正确做法

1
2
3
4
5
6
7
8
9
10
var aBtn = document.getElementsByTagName('input');
for( var i=0; i<aBtn.length; i++ ){
aBtn[i].onclick = function (){
// alert( i ); // undefined。 函数中有var声明i,因此不会向父级寻找。此时会预解析,即初始预解析为undefined
for( var i=0; i<aBtn.length; i++ ){ //如果里面的i不加var声明,则函数中就预解析不到i,因此向父级寻找i,即i=3;
aBtn[i].style.background = 'yellow';
}
};
}

例子八:

1
2
3
4
5
6
7
8
9
10
11
12
alert( fn1 ); // 火狐浏览器不能对下面的函数进行预解析
if( true ){
var a = 1;
function fn1(){
alert(123);
}
/*......代码........*/
}

推荐写法:把定义的变量和函数写在 if 外面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var a = 1;
function fn1(){
alert(123);
}
if( true ){
/*......代码........*/
}
//其中:
fn1(); //调用fn1函数
fn1; //不是调用
alert(fn1); //把函数块的全部内容弹出来

因此在事件调用的时候不要加括号:

1
2
3
4
5
6
7
8
oDiv.onclick = fn1;
//事件函数也可以写成如下形式:
oDiv.onclick = function (){
...
}