JS数据类型及函数的预编译
1.JS总体上分为:原始值和引用值
原始值分为:Number、Boolean、String、undefined、null;原始值不可改变的值,存储在栈【stack】的,先进后出!
引用值:array、Object、function、date、RegExp;原始值是可改变的,引用值大体是存储在堆【heap】中;
说明:一个变量经过声明,但是没有赋值,默认情况下就是undefined!例如:
1 <body> 2 <script type="text/javascript"> 3 var b; 4 document.write(b); 5 </script> 6 </body>
2.JS错误分为:低级错误【语法解析错误】和逻辑错误【标准错误,情有可原】,每个js语句后面都要以;【分号】结尾,但是函数、for循环、if语句后面不需要加分号;
①. 低级错误:
1 <script type="text/javascript"> 2 var a=1; 3 var b=2: 4 var c=3; 5 document.write(b); 6 </script>
像上面这种,本来b后面是需要加分号的,结果却是加上了冒号,所以这叫语法解析错误!
②.逻辑错误:
1 <script type="text/javascript"> 2 var a=1; 3 var c=3; 4 document.write(b); 5 </script>
上面这段代码明明没有变量b,但是我们这里愣是使用了document.write(b)将变量b的值输出了,这是逻辑错误!
3.一个html页面中可以使用多个js代码块,如下所示:
1 <body> 2 <script type="text/javascript"> 3 var a=1; 4 </script> 5 <script type="text/javascript"> 6 document.write(a); 7 </script> 8 </body>
我们从上面的js代码块中声明了变量a,但是是从下面的js代码块中输出了变量a,这和从一个代码块中声明a和输出a没有区别!要真说有没有区别呢?实际上也是有点的,如下所示:
1 <body> 2 <script type="text/javascript"> 3 var a=1; 4 document.write(c); 5 </script> 6 <script type="text/javascript"> 7 var b=2; 8 document.write(b); 9 </script> 10 </body>
这样有多个代码块的时候,当一个代码块中【上边的代码块】出了错误并不会影响下面代码块!
4.运算操作符:
+ 号,作用:
①.数学运算
②.任意数据类型和字符串相加都是字符串
5.凡是该返回一个数字的,但是又不知道这个数字是几的时候就会返回一个NaN【Not a Number】
eg:
1 <body> 2 <script type="text/javascript"> 3 var a = 0/0; 4 document.write(a); 5 </script> 6 </body>
如果是1/0,则返回值就是无限的Infinity,如下所示:
1 <body> 2 <script type="text/javascript"> 3 var a = 1/0; 4 document.write(a); 5 </script> 6 </body>
如果是-1/0则结果就是负无穷【-Infinity】,如下所示:
1 <body> 2 <script type="text/javascript"> 3 var a = -1/0; 4 document.write(a); 5 </script> 6 </body>
6.==是比较运算符,返回值为Boolean类型;如1==1,返回true;再例如:
1 <body> 2 <script type="text/javascript"> 3 a = Infinity == Infinity; 4 document.write(a); 5 </script> 6 </body>
返回值也是true;但是有一个特例:NaN,这家伙一急眼连自己都不认识,如下:
1 <body> 2 <script type="text/javascript"> 3 a = NaN == NaN; 4 document.write(a); 5 </script> 6 </body>
返回值为false;
7.&&【与】、||【或】、!【非】
&&:与运算符,当&&连接两个表达式或者数值的时候,先看&&号前面的数值,如果该值转换成布尔值为false,则直接返回该值,
如果该值转换为布尔值为true,则直接返回&&后面的值。eg:
1 <body> 2 <script type="text/javascript"> 3 a = 0 && 2; 4 document.write(a); 5 </script> 6 </body>
返回值就为0,如果是下面的这个例子:
1 <body> 2 <script type="text/javascript"> 3 a = 1 && 2+2; 4 document.write(a); 5 </script> 6 </body>
则返回值就为4;
所以:&&运算符连接两个表达式或者变量的时候,全真才是真,有一个为假,就返回假!
当&&连接多个表达式或者变量的时候,只要是&&前面的为真,那么它就往后走,直到遇到转换成布尔值为false的值的时候就直接返回该值【不用管真或者假】,如果直到最后都是真,那么返回最后一个值!如下:
1 <body> 2 <script type="text/javascript"> 3 a = 1+2 && 2+2 && 3-3 && 1+3; 4 document.write(a); 5 </script> 6 </body>
返回值就是0,而如下代码:
1 <body> 2 <script type="text/javascript"> 3 a = 1+2 && 2+2 && 3-2 && 1+3; 4 document.write(a); 5 </script> 6 </body>
返回值就是4;
注意:undifined、null、NaN、""【空串】、0、false 这六个值转换成Boolean值之后都是false!
||:或操作运算符,它和&&运算符的区别是:&&运算符遇到false的时候停止,而||或运算符是遇到true的时候返回,如果到最后没遇到为true的,那么就把最后一个返回!例如:
1 <body> 2 <script type="text/javascript"> 3 var a= 4 || 3; 4 document.write(a); 5 </script> 6 </body>
返回的是4,而如果是:
1 <body> 2 <script type="text/javascript"> 3 var a= 0 || 3; 4 document.write(a); 5 </script> 6 </body>
返回的就是3,如果是:
1 <body> 2 <script type="text/javascript"> 3 var a= 0 || false || null; 4 document.write(a); 5 </script> 6 </body>
返回的就是null;
!是取反的意思,这个非运算符会首先将运算符后面的表达式或者数字给转换成布尔值,然后取反操作,例如:
1 <body> 2 <script type="text/javascript"> 3 var a = 4; 4 document.write(!a); 5 </script> 6 </body>
返回值为false;
如果是:
1 <body> 2 <script type="text/javascript"> 3 var a = undefined; 4 document.write(!a); 5 </script> 6 </body>
则返回值为true;
当然javaScript中还有&、|,这个&、|是二进制的与、或【也就是会先把两边的数值转换成二进制,然后进行相与的操作或者或的操作,实际开发中几乎不用】!
在浏览器接收用户输入的数据,案例如下:
1 <body> 2 <script type="text/javascript"> 3 var score =parseInt(window.prompt("input")); 4 if(score == 100){ 5 alert("满分"); 6 } 7 </script> 8 </body>
if与&&的相互转换,如下所示:
1 <body> 2 <script type="text/javascript"> 3 if(2>1){ 4 document.write("哈喽"); 5 } 6 2>1 && document.write("哈喽"); 7 </script> 8 </body>
上面两种方式输出都是一样的,体现了if与&&的相互转换!
8.注释
javascript就两种注释【和java是一致的】,一种是单行注释//,另外一种是多行注释/**/
css注释:就一种注释,就是这种段落注释/*注释文字*/
html注释:一种:<!--注释文字-->
9.if.... else if...else语句:
1 <body> 2 <script type="text/javascript"> 3 var score = window.prompt("score"); 4 if(score > 90 && score <= 100){ 5 document.write('alibaba'); 6 }else if(score > 80 && score <=90){ 7 document.write('tencent'); 8 }else if(score > 70 &&score <=80){ 9 document.write('baidu'); 10 }else if(score < 60 ){ 11 document.write('are you kidding me?'); 12 }else{ 13 document.write('滚'); 14 } 15 </script> 16 </body>
10.for循环:
1 <script type="text/javascript"> 2 for(var i=1;i<10;i++){ 3 document.write(i); 4 } 5 </script>
11.while循环
1 <script type="text/javascript"> 2 var i=0; 3 while(i < 5){ 4 document.write(i); 5 i++; 6 } 7 </script>
do...while循环:
1 <body> 2 <script type="text/javascript"> 3 var i =window.prompt("变量i"); 4 do{ 5 document.write(i); 6 i++; 7 }while(i < 5) 8 </script> 9 </body>
12.switch...case语句
1 <body> 2 <script type="text/javascript"> 3 var i =window.prompt("变量i"); 4 switch(i){ 5 case "a": 6 console.log("result a"); 7 break; 8 case 2: 9 console.log("result 2"); 10 break; 11 case true: 12 console.log("result true"); 13 break; 14 default: 15 console.log("完了"); 16 } 17 </script> 18 </body>
13.arr数组【遍历、赋值、创建、length属性】
1 <body> 2 <script type="text/javascript"> 3 var arr=[1,2,3,4,5,"bad",undefined,true]; 4 for(var i=0;i<arr.length;i++){ 5 document.write(arr[i]); 6 } 7 arr[0]=false; 8 for(var i=0;i<arr.length;i++){ 9 document.write(arr[i]); 10 } 11 </script> 12 </body>
14.对象
1 <body> 2 <script type="text/javascript"> 3 var zhangsan={ 4 lastName:"小李", 5 age:13, 6 sex:true, 7 handsome:false 8 } 9 10 console.log(zhangsan.lastName); 11 zhangsan.lastName="张三"; 12 console.log(zhangsan.lastName); 13 </script> 14 </body>
15.typeof可以返回数据的类型,返回值【6个】可以是:Number、String、Boolean、Object、undefined、function;
对于Number数字类型、String字符串类型、及Boolean布尔类型没啥可说的,那啥时候返回Object类型呢?
对于{name:"张三"}【对象】、[1,2,"数字"]【数组】、及null,我们使用typeof测试的时候都是返回Object类型!如下所示:
1 <body> 2 <script type="text/javascript"> 3 document.write(typeof([1,"ds"])); 4 document.write(typeof(null)); 5 document.write(typeof({name:"张安"})); 6 </script> 7 </body>
1 <body> 2 <script type="text/javascript"> 3 var fun = function(){} 4 document.write(typeof(fun)); 5 </script> 6 </body>
返回值是function类型,如果我们对一个不定义或者定义为undefined,那么我们使用typeof进行类型检测的时候,也会返回undefined!
1 <body> 2 <script type="text/javascript"> 3 var fun = undefined; 4 document.write(typeof(fun)); 5 document.write(typeof(a)); 6 </script> 7 </body>
注意哦,Number(null)为0,如下所示:
1 <body> 2 <script type="text/javascript"> 3 var num = null; 4 document.write(Number(num)); 5 </script> 6 </body>
16.function函数:
①.函数声明
②.函数执行
代码案例:
1 <body> 2 <script type="text/javascript"> 3 function test(){ 4 document.write("哈哈"); 5 } 6 test(); 7 </script> 8 </body>
函数参数:
①.形式参数
②.实际参数
注意:形式参数的个数可以比实际参数的多,也可以少【类似于其它语言的可变参数】!实际上在形式参数中
有一个参数arguments的参数,这个参数默认会将所有的实参都接收!如下代码所示:
1 <body> 2 <script type="text/javascript"> 3 function test(a,b){ 4 console.log(arguments); 5 document.write(a); 6 document.write(b); 7 } 8 test(13,14,15); 9 document.write("<br/>"); 10 function testFunction(a,b){ 11 console.log(arguments); 12 document.write(a); 13 document.write(b); 14 } 15 testFunction(13); 16 </script> 17 </body>
当然也可以通过arguments.length查看实际参数的长度,而函数名.length获取形参的长度!
1 <body> 2 <script type="text/javascript"> 3 function test(a,b){ 4 console.log(test.length);//形参的长度 5 console.log(arguments.length);//实参的长度 6 } 7 test(13,14,15); 8 </script> 9 </body>
函数返回值:
17. JS运行的两大特点:
①.单线程
②.解释执行
18. JS代码执行流程【js执行的三部曲】:
1.语法解析【提前扫描一遍】
2.预编译
3.解释执行
我们现在就讲预编译:
如下所示:正常情况下,我先声明函数,然后再调用函数,是没有问题的,如下:
1 <body> 2 <script type="text/javascript"> 3 function testFunction(){ 4 document.write("Hello World!"); 5 } 6 testFunction(); 7 </script> 8 </body>
这样是没问题的,但是如果我们是如下代码:
1 <body> 2 <script type="text/javascript"> 3 testFunction(); 4 function testFunction(){ 5 document.write("Hello World!"); 6 } 7 </script> 8 </body>
将声明写在后面,函数调用写在前面,你发现也是没问题的,这是为什么呢?预编译在起作用!接着如果是变量的话,正常如下:
1 <body> 2 <script type="text/javascript"> 3 var a = 123; 4 console.log(a); 5 </script> 6 </body>
正常情况下这也是没问题的,先声明再调用!但是如下,如果我先调用再声明,如下:
1 <body> 2 <script type="text/javascript"> 3 console.log(a); 4 var a = 123; 5 </script> 6 </body>
你会发现返回值是undefined,但是没有报错哦,如果我的代码再变,如下:
1 <body> 2 <script type="text/javascript"> 3 console.log(a); 4 </script> 5 </body>
你会发现代码报错了!上面其实基本上都是预编译在起作用!
总结:1.函数声明整体提升【也就是无论函数在哪里声明的,js在预编译的时候会将这个函数的声明提到js代码的最前面】
2.变量 声明提升【也就是说:对于变量而言,预编译仅仅是声明提升到js代码的最前面,赋值不提升】,所以刚才先
打印a的时候有一个是undefined,但是不报错,因为声明在预编译的时候已经提到js代码的最前面了!
预编译前奏:
①.imply global 暗示全局变量:即任何变量,如果变量未经声明就赋值,此变量就为全局对象【window对象】所有!
例如: a = 123 就相当于window.a = 123;
1 <body> 2 <script type="text/javascript"> 3 a = 10; 4 console.log(a); 5 console.log(window.a); 6 </script> 7 </body>
再来个经典案例,并附有解释,如下:
1 <script type="text/javascript"> 2 function testFunction(){ 3 var a = b = 3;// 4 /* 5 注意赋值操作:一定是从右到做的一个顺序 6 也就是说:上述代码的执行顺序是:先将3赋值给b,然后再声明a,然后将b赋值给a,但是在这个过程中 7 缺少了一部声明b变量的过程,这就导致了b未声明,我们说未声明就赋值的变量是全局变量,此变量为 8 全局对象【window对象】所有!所以我们可以在控制台上访问window.b输出3,但是访问不 9 到window.a变量,因为a变量不是全局变量 10 */ 11 } 12 testFunction(); 13 </script> 14 </body>
②.一切声明的全局变量,全是window的属性,window就是全局的域【相当于一个仓库】!
例如:var a = 123; =====> window.a = 123;
也可以这样理解:var a = 123;就相当于
window {
a:123;
}
即相当于给window对象加了一个a属性,属性值为123;
其实:
1 <body> 2 <script type="text/javascript"> 3 a = 10; 4 console.log(a); 5 </script> 6 </body>
这里的console.log(a)就相当于console.log(window.a);
函数预编译【四步】:预编译的过程发生在函数执行的前一刻【也就是这个函数要执行了,但是还没执行的时候就先进行预编译操作,预编译之后函数才会一行一行的执行】!
①.创建AO对象【Activation Object:活动对象,也叫执行期上下文】,这个AO对象实际上就是这个函数产生的一个存储空间库,即:AO{ }!
②.找形参和变量声明,将变量名和形参名作为AO属性名,值为undefined!
③.将实参值和形参统一
④.在函数体里面找函数声明,函数声明的名作为AO对象的名,值赋予函数体
预编译之后进行函数的执行;
我们先来看看下面这个js函数的的执行,请说出执行结果:
1 <body> 2 <script type="text/javascript"> 3 function fn(a){ 4 console.log(a); 5 var a = 123; 6 console.log(a); 7 function a(){} 8 console.log(a); 9 var b = function(){}; 10 console.log(b); 11 function d(){} 12 } 13 fn(1); 14 </script> 15 </body>
我们先不说其执行结果是啥:我们来看看其执行过程,该函数执行之前先进行预编译操作,预编译之后再执行;
我们来说一下预编译的过程:
①.
创建AO对象,AO{
}
②.找形参和变量声明,将变量名和形参名作为AO属性名,这里找到了形参a和变量声明a和b,然后由于变量名和
形参名重复,所以只保留变量名a就ok了【变量在后面】,然后将形参和变量名作为AO对象的属性,值为undefined,
如下所示:
AO{
a:undefined,
b:undefined
}
③.将形参和实参相统一,这里的实参1就传递给了形参a,实际上也就是传递给了AO对象的属性a,所以AO对象变为:
AO{
a:1,
b:undefined
}
④.在函数体里面找函数声明,在上面的函数里面只有两个函数声明a和函数声明d,注意:函数b不是函数声明,
而是函数表达式,这是两个概念,需要区分;函数声明的名作为AO对象的名,值赋予函数体,如下所示:
AO{
a:function a(){},
b:undefined,
d:function d(){}
}
上面四步是预编译的四步,编译好了之后AO对象也就创建好了,这时候就可以进行函数执行了,函数执行的时候,貌似我们使用console.log(a)输出的是a,但是实际上输出的是AO对象中的a属性对应的值!所以上面的函数第一次打印的时候打印的是AO中的a,AO中的a属性对应的属性值是function a(){},所以此时我们用console.log(a)输出的就是function a(){},而接着一行一行的执行,又到了var a = 123;其实在预编译的时候的第二步,我们将形参和变量名都作为了AO对象的属性名,这其实本身就是变量声明的提升,变量声明已经在编译的时候提升上去了,所以在执行的时候又声明我这里就不用管了,但是此时a=123赋值还没有读呢,所以此时就会将AO对象的a属性赋值为123,然后我们接着使用console.log(a);输出的时候输出的就是AO对象中的a的属性值123,同理:function a(){}在预编译的时候也已经提升上去了,所以这里在执行的时候就不用看了,然后继续执行后面的console.log(a);再次输出123,然后执行下面的var b = function(){};实际上这里仅仅是执行了给AO对象的属性b的赋值,即:
AO{
a:function a(){},
b:function(){},
d:function d(){}
}
并没有变量b的声明,因为变量b的声明在预编译的时候已经提升上去了,所以此时console.log(b)输出的就是function(){};到此函数执行结束!
所以上面函数的输出值为:
这道题的复杂程度包含:形参名和变量名和函数名重复的问题,所以我们需要解决一个优先级顺序的问题,包括:谁覆盖谁的问题,再包括执行顺序是影响变量还是函数啦这样的一些问题,什么能解决这样的问题呢?就是预编译能解决这样的问题!
依据上述预编译及执行顺序,我们再来看一个例子,如下所示:
1 <body> 2 <script type="text/javascript"> 3 function test(a,b){ 4 console.log(a); 5 c = 0; 6 var c; 7 a = 3; 8 b = 2; 9 console.log(b); 10 function b(){} 11 function d(){} 12 console.log(b); 13 } 14 test(1); 15 </script> 16 </body>
代码的执行结果为:
注意:只有函数声明可以提升,而函数表达式是不可以提升的!再来检测一例子:
1 <body> 2 <script type="text/javascript"> 3 function test(a,b){ 4 console.log(a); 5 console.log(b); 6 var b = 234; 7 console.log(b); 8 a = 123; 9 console.log(a); 10 function a(){} 11 var a ; 12 b = 234; 13 var b = function(){} 14 console.log(a); 15 console.log(b); 16 } 17 test(1); 18 </script> 19 </body>
注意:预编译不仅发生在函数里,还发生在全局!如下所示:
1 <body> 2 <script type="text/javascript"> 3 var a = 123; 4 console.log(a); 5 </script> 6 </body>
正常情况下,输出a的值是123是没问题的,而如果我将console.log(a)提到前面,那么如下:
1 <body> 2 <script type="text/javascript"> 3 console.log(a); 4 var a = 123; 5 </script> 6 </body>
此时,输出a的值为undefined,因为变量声明是可以提前的!而如果我将var a =123,直接去掉,那么如下:
1 <body> 2 <script type="text/javascript"> 3 console.log(a); 4 </script> 5 </body>
此时就会报错了,因为没有这个变量嘛!如果我是如下写法:
1 <body> 2 <script type="text/javascript"> 3 console.log(a); 4 var a = 123; 5 function a(){} 6 </script> 7 </body>
此时输出的a的值就是函数a了!这些其实都是预编译的结果!
对于全局的预编译而言实际上就三步【和函数的预编译相比缺少了:实参给形参传值这一步】:
①.创建GO对象【Global Object:全局对象,也叫执行期上下文】,这个GO对象实际上就是这个函数产生的一个存储空间库,即:GO{ }!
②.找变量声明,将变量名和形参名作为GO属性名,值为undefined!
③.在函数体里面找函数声明,函数声明的名作为GO对象的名,值赋予函数体
所以,做个小测试,如果我将console.log(a)输出a的时候,你就会看到输出的a的值是123,如下所示:
1 <body> 2 <script type="text/javascript"> 3 var a = 123; 4 function a(){} 5 console.log(a); 6 </script> 7 </body>
注意:这里的GO实际上就是window对象,也就是:GO == window;所以我们在全局定义好了变量自动就会存储到GO对象中,而GO对象就是window对象,所以我们可以直接使用window对象来访问这些属性,显然是没问题的!实际上GO和window是同一个事物的两个名称而已!所以我们看到下面两句实际上是一样的:
console.log(window.a);//这个window.a实际上也就是GO.a
console.log(a);//这个a会自动去GO对象中去拿的
上面我们讲到一个变量未经声明就赋值,那这个变量是属于全局变量,该全局变量是属于全局对象window所拥有的,换句话说:一个变量未经声明就赋值,实际上这个变量是放入到GO中去预编译的,如下代码:
1 <body> 2 <script type="text/javascript"> 3 function test(){ 4 var a = b = 4; 5 } 6 test(); 7 console.log(b); 8 console.log(a); 9 </script> 10 </body>
b的值输出的是4,而输出a的时候报错了,需要注意:函数必须执行,如果不执行,即如果没有【test()】,那么不会有预编译,函数没有经过预编译,那么b的值也是输出不了的,这点千万要注意!而且上述代码相当于:
AO{
a:4;
}
GO{
b:4;
}
即:b归GO对象拥有,而a变量归AO对象拥有!所以我们在test()函数内部如果输出window.b是没有问题的,因为window==GO,而如果我们输出window.a则不输出不了,如下所示:
1 <body> 2 <script type="text/javascript"> 3 function test(){ 4 var a = b = 4; 5 console.log(window.b); 6 console.log(window.a); 7 } 8 test(); 9 </script> 10 </body>
输出结果如下所示:
再引申一下给大家,你们说是先生成AO呢还是先生成GO呢?答案是:肯定是先生成GO对象啊,代码如下所示:
1 <body> 2 <script type="text/javascript"> 3 console.log(test); 4 function test(test){ 5 console.log(test); 6 var test = 234; 7 console.log(test); 8 function test(){} 9 } 10 test(1); 11 var test = 123; 12 </script> 13 </body>
代码会怎样输出呢?
代码会首先进行全局编译,然后按着整体编译的三步曲,生成GO对象,如下:
GO{
test:undefined;
}
紧接着将函数提升,得到如下所示:
GO{
test:function test(){}
}
然后就开始执行代码:
先执行console.log(test);此时的test其实就是上面GO对象的属性test,值为function test(){};接着就越过函数test,因为刚才在整体预编译的时候就已经将函数test提升了,继续往下走,执行test(1),但是在执行test(1)之前的一刻会有一个预编译的过程,编译test函数,首先生成AO对象,然后将参数和变量名作为AO对象的属性,如下所示:
AO{
test:undefined
}
变成:
AO{
test:123
}
然后将函数实参和形参统一【即传值】,如下:
AO{
test:1
}
然后,我们可以紧接着函数名提升,并赋值:
AO{
test:function test(){}
}
上面是函数预编译的过程,预编译之后就开始执行函数代码,首先执行console.log(test)输出test属性,但是这个test属性是用AO对象的呢还是使用GO对象的呢,毕竟这两个对象里面都有test属性,这个时候AO对象和GO对象就会形成一个链条,这个链条以近的为主,如果AO有就使用AO对象的,AO没有的情况下在使用GO的,所以此时会打印AO对象的test属性,得到函数体function test(){},然后紧接着执行test=234赋值操作,然后执行下一条输出console.log(test);此时输出的就是234,所以最终结果就是:
再来下面的一端代码我们来检验一下学习情况:
1 <body> 2 <script type="text/javascript"> 3 global = 100; 4 function fn(){ 5 console.log(global); 6 global = 200; 7 console.log(global); 8 var global = 300; 9 } 10 fn(); 11 var global; 12 </script> 13 </body>
其过程如下:
首先是整体预编译,【三部曲】,先生成GO对象,然后将变量和函数提升及函数赋值,得到如下:
GO{
global:undefined;
fn:function fn(){}
}
整体预编译之后,然后整体执行,先执行global =100,则转变为:
GO
{
global:100;
fn:function fn(){}
},由于函数声明在整体预编译的时候已经提升了,所以这里不管,接着执行下面的fn()调用函数fn,注意,在调用函数fn的前一刻,我们先执行预编译的操作,预编译fn,预编译函数【函数编译四部曲】,首先生成AO对象,然后变量提升,传递参数,函数提升并赋值,如下所示:
AO{
global:undefined;
}
这是函数预编译的四部曲,预编译之后,紧接着进行函数的执行,执行console.log(global),由于AO对象存在global属性,所以这里输出的是AO对象的global属性【当然如果AO对象不存在global属性,会找到GO对象的global属性,打印出来】,所以输出undefined,然后继续执行global = 200,此时AO对象的global属性就变成了200,然后执行console.log(global)输出此时的200,最后再执行global = 300,就会将AO对象的global属性改为300,此时总体执行结束,所以最终的输出结果为:
再来一段代码,看输出结果:
1 <body> 2 <script type="text/javascript"> 3 function test(){ 4 console.log(b); 5 if(a){ 6 var b = 100; 7 } 8 c = 234; 9 console.log(c); 10 } 11 var a ; 12 test(); 13 a = 10; 14 console.log(c); 15 </script> 16 </body>
需要注意:函数在预编译的时候根本不会看if语句,不管它,直接将if语句内部的var b变量声明提升就OK了,之所以不看【不管】是因为还没有到函数执行呢,这里仅仅是预编译,所以根本不用判断是不是a为true!最终结果如下所示:
再来做两个测试:
1 <body> 2 <script type="text/javascript"> 3 function bar(){ 4 return foo; 5 function foo(){} 6 var foo =11; 7 } 8 console.log(bar()) 9 </script> 10 </body>
这个输出的就是函数foo,如下所示:
1 <body> 2 <script type="text/javascript"> 3 console.log(bar()); 4 function bar(){ 5 foo = 10; 6 function foo(){} 7 var foo = 11; 8 return foo; 9 } 10 </script> 11 </body>
输出的结果是:11,如下所示:
所以讲到这里如果我们再说:
1.函数声明,整体提升
2.变量 声明提升
大家就懂啥意思了!
注意:如下所示,区分开来哦:
1 <body> 2 <script type="text/javascript"> 3 console.log(b); 4 var b = function(){} 5 </script> 6 </body>
输出的b是undefined,这个属于变量,声明提升,而
1 <body> 2 <script type="text/javascript"> 3 console.log(b); 4 function b(){} 5 </script> 6 </body>
输出的b是函数b(){},这个属于函数声明,整体提升,所以var a = function(){};这叫函数表达式,不叫函数声明,这是两个概念,要区分开来!
再来做一个复杂点的:
1 <body> 2 <script type="text/javascript"> 3 a = 100; 4 function demo(e){ 5 function e(){} 6 arguments[0] = 2; 7 console.log(e); 8 if(a){ 9 var b = 123; 10 function c(){ 11 //豬都能做出來 12 } 13 } 14 var c ; 15 a = 10; 16 var a; 17 console.log(b); 18 f = 123; 19 console.log(c); 20 console.log(a); 21 } 22 var a; 23 demo(1); 24 console.log(a); 25 console.log(f); 26 </script> 27 </body>
需要注意:arguments,是指的实参,而arguments[0]是与demo函数的第一个参数e同步的,当arguments[0]的值发生变化之后,实际上也就是demo的第一个参数e的值发生了变化,所以最后GO对象和AO对象的最终结果如下所示:
还有一点注意:新版本的谷歌浏览器中if语句内部不能再嵌套函数了,在老版本的浏览器中是可以的,所以这里输出c的时候按正说应该是输出function()函数对象的,正是由于if语句内部不能嵌套函数,所以这里输出的是undefined值!所以最终AO和GO对象为:
GO{
a:100;
demo:function(){},
f:123
}
AO{
e:2,
b:undefined,
c:undefined【按正常推理应该是function(){}】
a:10
}
最终的结果为: