1. 作用域
域:空间、范围、区域。。。
作用:读、写
alert(a);
var a=1;
结果为undefined
alert(a);
var a=1;
直接报错(因为没有找到var)
浏览器:JS解析器。
1)找一些东西:var function 参数
① 找到a=未定义(undefined),
所有的变量,在正式运行代码之前,都提前赋了一个值:未定义(undefined)。
② fn1 = function fn1(){alert(2); }
所有的函数,在正式运行代码之前,都是整个函数块。
以上叫做JS的预解析。注意两点:
- 遇到重名的,只能留一个
- 变量和函数重名,就只能留下函数。都是函数时,后面的覆盖前面的。
2)逐行读代码
表达式:= + . * / % ++ – ! ,参数…..Number();
- 表达式会修改预解析的值
- 函数声明不会改变仓库的值,只是会声明一下。
alert(a); //未定义
var a=1;
function fn1(){
alert(2);
}
面试题
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
function a(){alert(4);}
alert(a);//3
a();//报错;因为现在仓库里面是a=3;所以a()相当于3();
2. 全局与局部作用域解析、作用域链
script 全局变量 全局函数 是自上而下运行的,
函数,是由里到外
如下所示,上面的script找不到下面的值,下面的可以找到上面的。
<script></script>
<script></script>
面试题1
var a=1;
function fn1(){
alert(a);//undefined
var a=2;
}
fn1();
alert(a);
分析:
1)预解析:
- 首先找到a=…(未定义)
- 然后找到function fn1(){
alert(a);
var a=2;
}
2)逐行解读代码
- a=1;
- 读到函数,因为只是一个函数声明,不是函数表达式,没有办法进行修改,所以仓库里面还是a=1;
- fn1();读到函数调用,就是让函数执行,函数是一个局部的域,遇到域就要预解析和逐行解读代码。首先预解析a=…,然后正式读代码,次数的a和全局的a没有任何关系。
- alert(a);找到的是全局的a,即为1.
面试题2
var a=1;
function fn1(){
alert(a);//1
a=2;
}
fn1();
alert(a);//2
分析
1)预解析:
- a=…(未定义)
- 找到函数声明
function fn1(){
alert(a);//undefined
a=2;
}
2)逐行解读代码
- a=1;
- 函数声明不用动。
- fn1();函数调用,又要预解析,逐行解读代码,弹出1。解释如下
此处需要注意,因为fn1()函数中找不到var,也找不到函数,所以预解析就结束了了,直接逐行解读代码。alert(a)的时候,因为找不到a,所以从子级跑到父级去找a(这个就就做作用域链,这里体现出了函数的由里到外)所以找到了,var a=1;弹出1,读到a=2的时候,因为局部的变量有能力改变外面的变量,所以,仓库中的a=1,变成a=2;
- alert(a);弹出2
总结一下,之所以fn1(){…}函数中,前者弹出undefined,后者弹出1,是因为前者进行了函数预解析,所以按照一般情况进行。第二个因为没有走预解析,在里面没有找到东西,所以跑到外面去寻找变量。不过此处我的理解是,在fn1(){…}中因为a=2,前面没有加var,所以相当于全局变量,这样理解就比较清楚了。
面试题3
var a=1;
function fn1(a){
alert(a);//undefined
a=2;
}
fn1();
alert(a);//1
分析
1)预解析
- var a=…
- 找到函数声明
2)逐行解读代码
- a=1;
- 函数声明不用管
- fn1();
首先找var a,没有找到,函数也没有,接着找参数,找到了(参数的本质就是一个局部变量),但是fn1();并没有传这个参数,所以这个参数相当于未定义。所以fn1(){...}的预解析就找到了一个参数 a =...。接着逐行解读代码,alert(a),读到的应该是预解析的参数,所以是undefined,读到a=2时,参数由未定义变成2,全局的变量a=1并没有改变
- alert(a)读到应该是全局的a,所以值为1
面试题4
var a=1;
function fn1(a){
alert(a);//1
a=2;
}
fn1(a);
alert(a);//1
分析
1)预解析
- var a=…
- 找到函数声明
2)逐行读代码
- a=1
- 函数声明
- fn1(a)函数的调用
a.预解析 var没有,函数没有,参数有,为a=...;
b.逐行读代码,参数相当于局部变量,所有从参数处读。参数也相当于赋值,此处a=1,但是这个1并不是全局的那个a,而是参数的那个a。紧接着这个局部的a由1变成2
-alert(a)指的是全局的a,为1
3. 调用局部数据、全局声明、for嵌套函数中i取值
1)局部可以访问外部,全局变量任何函数都可以改它。如下
var num=0;
function fn1(){
num++;
}
fn1();//1
2)外部不可以访问函数内部的作用域。如下所示,会报错
function fn1(){
var a='大鸡腿';
}
fn1();
alert(a);
要向获取函数内的值
方法1:可以通过定义全局变量的方法如下所示
var str='';
function fn1(){
var a='大鸡腿';
str=a;
}
fn1();
alert(str);
方法2:局部的函数调用
function fn2(){
var a='钻石';
fn3(a);
}
fn2();//'钻石'
function fn3(a){
alert(a);
}
3)作用域
函数的{}才是一个作用域,if(){}的{},for(){}中的{}不属于域。域的标志是先解析后执行
注意:
所以,想定义全局函数和全局变量尽量不要写在if或者for的{}里面
4)一道神奇的题
<input type="button" value="按钮1"/>
<input type="button" value="按钮2"/>
<input type="button" value="按钮3"/>
实现一个效果,点击任意一个按钮,是它们的背景色都变成黄色。以下代码没有问题
<script>
window.onload=function(){
var aBtn=document.getElementsByTagName('input');
for(var i=0;i<aBtn.length;i++){
aBtn[i].onclick=function(){
for(var i=0;i<aBtn.length;i++){
aBtn[i].style.background='yellow';
}
}
}
}
</script>
思考1:为什么不按照下面的方法写。分析:下面方法会报错。因为 alert(i);为3。for(var i=0;i
<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>
思考2:alert(i);//?是多少呢?是undefined。因为aBtn[i].οnclick=function()这个函数会首先预解析,var i=…所以alert(i)为undefined
<script>
window.onload=function(){
var aBtn=document.getElementsByTagName('input');
for(var i=0;i<aBtn.length;i++){
aBtn[i].onclick=function(){
alert(i);//undefined
for(var i=0;i<aBtn.length;i++){
aBtn[i].style.background='yellow';
}
}
}
}
</script>
思考3:把后面那个for循环中的var 去掉。所以alert(i);需要跑到父级去找,此时已经为3了
<script>
window.onload=function(){
var aBtn=document.getElementsByTagName('input');
for(var i=0;i<aBtn.length;i++){
aBtn[i].onclick=function(){
alert(i);//3
for(i=0;i<aBtn.length;i++){
aBtn[i].style.background='yellow';
}
}
}
}
</script>