一、函数的定义和调用
1.JavaScript函数的本质(类型 function)
- JavaScript将函数看做是一种类型值,与其他类型地位相同,凡是可以赋值的地方就可以使用函数。
- 所以var a=某一个函数这种赋值方式是正确的。
- 正因为如此JavaScript函数在使用上非常灵活
2.函数的声明
function命令:function后面跟函数名,接下来是由圆括号包围的参数列表,函数体放在花括号内部。
function print(s){
console.log(s);
}
直接使用表达式法:将函数定义直接写在等号右侧即可,这也是JavaScript将函数当成一种类型的典型表现
var print =function(s){
console.log(s);
}
3.函数调用
<script type="text/javascript">
function sum(a,b){
return a+b;
}
var f2=function(a,b){
return a*b;
}
console.log(sum(5,7));//----输出为:12
console.log(f2(5,7));//-----输出为:35
</script>
注意事项:
- JavaScript没有重载,如果函数名称相同,会调用后定义的。
- 如果函数中不写return语句,或return语句后面无内容,则函数的返回值为undefined。
二、函数属性
1.函数的属性和方法
- 在JavaScript函数中,有一些特定的元素,他们被称为属性和方法
- 这些元素不仅有预定义的,还可以自定义,自定义的属性和方法会成为JavaScript面向对象的实现方式
- 预定义的属性有
name:获取函数名称
<script type="text/javascript">
function f1(){}
console.log(f1.name);//f1
var f2=function(){}
console.log(f2.name);//f2
var f3=function myname(){}
console.log(f3.name);//myname
</script>
length属性:返回函数预期传入的参数个数
<script type="text/javascript">
function sum(a,b){
return a+b;
}
var f2=function(a,b,c){
return a*b+c;
}
console.log(sum.length);//----输出为:2
console.log(f2.length);//-----输出为:3
</script>
toString方法:返回函数源代码(函数内部的注释也可以返回)
<script type="text/javascript">
function sum(a,b){
return a+b;
}
console.log(sum.toString());
</script>
2.函数的作用域
-----定义:作用域(scope)指的是变量存在的范围。在ES5的规范中,JavaScript只有两种作用域:一种是全局作用域,变量在整个程序中一直存在,所有的地方都可以读取;另一种是函数作用域,变量只存在函数内部。
-----函数外部声明的变量就是全局变量,他可以在函数内部读取。
-----在函数内部定义的变量,外部无法读取,称为“局部变量”。
<script type="text/javascript">
var a;//全局变量
function fun(){
a=2;//若加上var则结果变为undefined
}
console.log(a);//输出为:undefined
fun();
console.log(a);//输出为:2
</script>
------函数内部定义的变量,会在该作用域内覆盖同名的全局变量(局部变量不会污染全局变量,但是局部变量命名时要谨慎)
<script type="text/javascript">
var a=1;
function fun(){
var a=2;
}
fun();//若想要输出2---console.log(fun())且在fun()函数中要添加return a;
console.log(a);//输出为1;若函数中的var去掉,则输出为2,局部变量污染全局变量
</script>
3.let和const
-----在ES5里只有var,在ES6中新增的定义变量的关键字let。
-----let将变量的作用域局限在“块”内,而var则将变量的作用域局限于函数内部。
-----let不能重复声明,而var可以。(写的let只能在花括号里面操作)
-----const常量
var a=1;
var a=0;//合法
let b=0;
let b=1;//非法
<script type="text/javascript">
if(true){
let a=1;
var b=1;
}
console.log(b);//--输出1
//等价于上面的代码
var b;
if(true){
let a=1;
b=1;
}
console.log(b);//--输出1
console.log(a);//报错
</script>
三、let实例
实例一
<script type="text/javascript">
var a=[];
//var i;等价于把 i 提前声明到此处
for(var i=0;i<5;i++){
a[i]=function(){
console.log(i);
}
}
a[1]();
a[2]();
a[3]();
console.log("--------------------");
var b=[];
for(let j=0;j<5;j++){
b[j]=function(){
console.log(j);
}
}
b[1]();
b[2]();
b[3]();
</script>
重要补充说明:
- 变量提升:当用var 声明一个变量的时候,编译器默认把声明的语句提到语句块的最前面,在哪里声明,就提到谁的前面(例如:在var a=[];后声明 var i;)
- a[i]=functiion----在JavaScript中来说,此处的function是函数的声明,声明需要用到 i ,但是此处仅仅只是声明,没有使用;当循环结束的时候 i 等于5,所以当最后使用的时候,输出结果为5,这里的 i 是全局的,只有一个地址。
- var修饰的变量是当前域的变量
- let具有的特性,是一个块级元素,这里的每个 i 都有自己的地址,互不影响
实例二
<script type="text/javascript">
console.log(a);
var a=1;
console.log(b);
let b=2;
</script>
等价于上面的代码
<script type="text/javascript">
var a;
console.log(a);
a=1;
console.log(b);
let b=2;
</script>
补充说明:
- 产生这种效果的原因是var的变量提升效果
实例三
<script type="text/javascript">
var a=1;
function fun(){
console.log(a);
if(false){
var a=2;//重新定义了局部变量var a,变量提升到了域的顶部
}
}
fun();//结果为undefined
</script>
等价于下面的代码:
<script type="text/javascript">
var a=1;
function fun(){
var a;
console.log(a);
if(false){
a=2;
}
}
fun();
</script>
四、arguments
1.函数的参数
- 定义:函数运行的时候,有时候需要提供外部数据,不同的外部数据会得到不同的结果,这个外部数据就叫做参数。
- 函数参数不是必须的,JavaScript中允许省略参数
- 注意:只有参数列表中靠后的参数可以省略,靠前的不能省略,如果想省略必须现实输入undefined。
- JavaScript中函数没有重载
<script type="text/javascript">
function f(a,b){
return a;
}
f(undefined,2);//如果此处不写undefined省略,会报错
console.log(f(,2));//报错
console.log(f(1,2));//1
console.log(f(2));//2
console.log(f(1,2,3));//1
</script>
2.arguments对象
- 定义:由于在JavaScript允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数,这就是arguments对象的由来。
- arguments对象包含了函数运行时的所有参数,arguments[0]就是第一个参数,arguments[1]就是第二个参数,以此类推。这个对象只有在函数体内部,才可以使用。
- 需要注意的是arguments虽然很像数组,但它是一个对象。数组专有的方法(比如slice和forEach),不能在arguments对象上直接使用
- 如果要让arguments对象使用数组方法,真正的解决方法是将arguments转为真正的数组。
- arguments输出的是传递的参数,并且以数组的形式,length输出的是函数定义的参数的个数
<script type="text/javascript">
var f=function(one){
console.log(arguments[0]);
console.log(arguments[1]);
console.log(arguments[2]);
console.log(arguments[3]);
}
f(4,2,3)
</script>
3.借助arguments对象,模拟函数重载
<script type="text/javascript">
var f=function(a,b){
if(arguments.length==2){
return a+b;
}else{
return b;
}
}
console.log(f(1,2));//返回值为3
console.log(f(1,2,3));//返回值为2
</script>
4.callee属性
arguments对象带有一个callee属性,返回它所对应的原函数
<script type="text/javascript">
var f=function(a,b){
console.log(arguments.callee===f);
}
f();//true
</script>
五、闭包
1.闭包
有权访问另一个函数作用域内变量的函数都是闭包。闭包就是一个函数引用另一个函数的变量,因为变量被引用着所以不会被回收,因此可以用来封装一个私有变量。
//未使用闭包
<script type="text/javascript">
function autoCount(){
let count=0;
console.log(count);
return count+=1;
}
var counter=autoCount;
counter();//输出0
counter();//输出0
counter();//输出0
</script>
//使用闭包,变量不会被销毁
<script type="text/javascript">
function autoCount(){
let count=0;
return function(){
console.log(count);
return count+=1;
}
}
var counter=autoCount();
counter();//输出0
counter();//输出1
counter();//输出2
</script>
六、IIFE
1.立即调用的函数表达式(IIFE)
- 在JavaScript中,圆括号()是一种运算符,跟在函数名之后,表示调用该函数。比如print()就表示调用print函数。
- 有时需要在定义函数之后,立即调用该函数。这时,不能在函数的定义之后加上圆括号,这会产生语法错误。解决方法有下面两种
方法一:(function(形参){/* code /}(实参))
方法二:(function(形参){/ code */})(实参)
<script type="text/javascript">
(function(value){
alert(value);
})("HELLO IIFE");
</script>
好处:一是不必为函数命名,避免污染全局变量;二是IIFE内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。