JavaScript基础四
函数
JavaScript当中,语句是构成代码的基本单位,而函数又是构成代码段(结构)的基本单位(函数就是一个片段的代码)
ECMAScript:函数是可以封装任意多条语句,而且可以在在码的执行过程当中,任意多次的调用去执行的,我们叫函数
- 函数是需要去封装的,
function
- 封装以后的函数如果不去调用,它是不会执行的
- 调用函数的时候,我们是通过函数的名子+
()
来完成的,如果有参数,则在括号时面写义相应的参数,参数与参数之间通过,
逗号去隔开
函数是通过关键字function去声明,它的语法格式如下所示
function 函数名(参数1,参数2...){
//要封装的代码
}
在EMCAScript里面,我们的函数也叫方法
在JavaScript当中,我们的创建一个方法除了使用上面真用function去定义以外,还可以使用另一种方式去封装一个方法,格式如下
var 方法名=function(参数1,参数2...){
//要封装的方法
}
注意事项:上面的两种方法都可以创建(封装)我们的方法
请看下面的例子
a();
b();
var b=function(){
document.writeln("吃了没,世界");
}
function a(){
document.writeln("hello world");
}
说明:在上面的代码当中,我们执行
b()
的方法的时候,它会报错,因为这个时候的b是通过我们的关键字var定义出来的,根据代码的运行顺序,最先出来的是function所以a()
它不会出问题,而调当我们调用b()
的时候,这个时候的了它只是定义了,但是没有赋值,它还是undefined,这个时候,它就会报undefined is not a functioin
的错误信息
函数的参数
参数是方法在定义的过程当中,所需要指定的变量,它定义的时候,直接写在function后面的括号里面,如下代码所示
function sayHello(name){ //这一个时候,这个name它就是参数,参数的定义,不需要添加关键字
//var name; //如果定义在这里,叫局部变量
document.writeln("大家好,我是:"+name);
}
sayHello("张三");
sayHello("李四");
小提示:我们的参数其实与变量意义差不多,都是在方法的内部使用,在方法内部定义变量的时候,我们使用
var
,代码写在方法体里面,而参数则写在function
后面的括号里面,并且不需要任何关键字
参数的类型
形参
形参指的是形式参数,也就是在定义方法的时候,定义的那一个参数,如下所示
function abc(n){
console.log(n);
}
说明:上面的代码当中,我们的n就是形式参数,形式参数统一没有默认值(undefined)
实参
实参指的是方法在调用的过程当中,我们传进去的参数,如下代码所示
function abc(n){
console.log(n);
}
abc("王五");
说明:上面的代码当中,我们的“王五”它就是实际参数,因为这个参数它是一个实际的值
在方法调用执行的过程,我们的参数值的传递方向是由实参传向形参
arguments参数组和
arguments
它是一个参数组合,它也是方法里面一个内置的变量,只有在function
的内容才能去调用它,它在function
里面默认会存在,不需要去自己去定义
arguments
与我们的方法的参数有关,它里面存放的是我们在调用方法的时候,传进去的参数(实参),它是我们方法参数(实参)的集合
关于形参数个与实参个数不一致的情况
-
形参个数大于实参个数
前面的参数会一一对应,后面没有赋值的参数为undefined
-
形参个数小于实参个数
所有的形参与实参都会一一对应,但是多于的实参不能够参过形参名去调用,只能通过arguments+序号
-
形参个数等于实参个数
这个时候,形参与实参一一对应
案例
现在要求写一个方法,这个方法是把所有的参数进行相加(只考虑数字),然后把得出来的和打印出来
function plus(){
//形参的个数是0
// var sum=arguments[0]+arguments[1]+arguments[2]+arguments[3]+arguments[4]+arguments[5];
// document.writeln(sum);
//
var sum=0;
for(var i=0;i<=arguments.length-1;i++){
sum=sum + arguments[i];
}
document.writeln(sum);
}
plus(2,6,9,11,45,23);
方法没有重载(overload)
概念:方法重载指的在定义方法的时候,有多个相同名子的方法,但是这些方法的参数类型或参数个数不相同
思考:为什么在JavaScript里面,我们没有方法重载这个概念?
- 在JavaScript里面,JS的数据类型是一个弱类型,这个参数具体是什么类型,由调用的时候,传入的值决定(没有参数类型的概念)
- 我们的JS方法里面,实参的个数与形参的个数可以不一致,它是由arguments来决定的,所以,我们在定义方法的时候,如果定义了N个形参,但是在调用的时候,并不一定非要传入N个实参(没有参数个条的概念)
**注意:**当两个方法名相同的时候(一个方法名定义了两次的时候,应该怎么处理)
function abc(){
console.log("这是第一个方法");
}
function abc(a){
console.log("这是第二个方法");
}
abc();
上面的代码以第二个方法为主(后出来的为主)
方法的返回值
在我们的方法里面,有一个关键字return
,当我们在代码在方法内部一旦遇到这个关键字的时候,这个方法就会立即中止,然后跳出这个方法
所有的方法,如果没有明确的添加return ,那么,则会隐式的在代码的最后加上一个return
,代表这个方法的结束
return在默认的情况之下,它结束这个方法的时候,返回到外边的值是undefined,我们可以在return的后面添加我们要返回到外边的值
案例
/*
* 现在要求写一个方法,这个方法是把所有的参数进行相加(只考虑数字)
* 然后把得出来的和交给外边,由外边处理
*/
function plus(){
var sum=0;
for(var i=0;i<arguments.length;i++){
sum+=arguments[i];
}
return sum; //结束方法,把值交给外边
}
var t =plus(1,3,5,7,9,2,5,6,7);
//这个时候的变量t就是plus方法调用以后的结果,这个结果通过return 返回到了外边
// document.writeln(t);
// console.log(t);
alert(t);
上面的代码当中,我们的
plus
这个方法,仅仅是把传进去的参数求和,求和完成以后,再把得到的值给外边,至于外边拿到这个值以后要干什么,plus
管不到
实参与形参的影响
在方法调用过程当中,值是由实参向形参发生传递,如果传递进去的值发生改变以后,会不会影响外边的值呢?
var a=10;
var b=20;
function abc(x,y){
x=x-1;
y=y-2;
console.log(x);
console.log(y);
}
abc(a,b);
//参数的值是由实参传向形参
//a-->x b--->y
document.writeln(a);
document.writeln(b);
上面的代码执行完毕以后,在控制台打印了x与y的值 分别是9,18,而在页面打印了a与b的值,分别为10,20,这说明这两个值没有受到影响。这就说明,实参不会因为形参在方向内部发生了改变页改变
总结:形参在方法内部的改变不影响外边的实参(这个实参并且是基本类型)
TODO:对向是引用传递,在这里,会受到影响
方法的优点
- 方法一旦封装完成以后,可以任意多次的调用,并用可以单独的成一个JS文件,通过这个JS文件,可以让它在不同的页面去调用(提高开发效率)
- 高类聚,低耦合
函数的递归
概念:当一个方法在内部又去调用了自己,这一种情况,我们叫方法的递归
下面请看一个例子
案例
写一个算法,计算从1到100之间的求和(不能使用任何循环语句去完成)
var sum=0; //保存计算结果
var i=1; //初始条件
function add(){
sum=sum+i; //要执行的代码
i++; //自变量
//还差一个循环的结束条件
if(i<=100){
add();
}
}
add();
document.writeln(sum);
说明:在上面的代码当中,我们在定义
add()
的方法的时候,又在这个方法的内部调了自身,这种情况, 我们就叫递归注意:在使用递归的时候,一定要注意它的调用条件(结束条件),如果处理不好,就会形成死循环
函数表达式
在JS里面定义函数(方法)的时候,大家都知道有两种方法,第一种是直接调用function
关键字去定义,第二种则是通过变量赋值的方法,把一个function
赋值给某一个变量
var a=function(){//......}
像上面这种情况的定义函数 ,我们就叫函数表达式
在我们使用函数表达式的时候,我们一旦把函数定义出来,就可以直接调用,具体如下
var b=function(x,y){
document.writeln(x+y);
}(11,45)
在上面的代码当中,我们定义完函数b以后,这个时候,我们直接在这个function的结尾添加了一对
()
,这个时候,当这个函数定义完成以后,就会直接被调用后期,我们会根据这一个函数表达式,来演变出一种写法:闭包
函数调用以及函数引用
函数调用
function a(){
document.writeln("我是方法a");
}
function b(){
a();
document.writeln("我是方法b");
}
// a();
// b();
//现在,我们都知道,我们可以直接去调用a,也可以直接去调用b
//要求:方法a不能够被别人调用,只能够通过方法b去调用方法a
b();
针对上面的代码,我们方法a不能被其它人调用(不能被外部直接调用,只能对它方法b去调用,这个时候,应该怎么办呢)
第一种方案,通过作用域来控制
我们都知道,在JS当,只有方法function才有作用域,这个时候,我们可以把方法a定义在方法b里面,如下所示
function b(){
function a(){
var str="hello world";
document.writeln("这是方法a");
}
a();
}
b();
因为方法a定义在了方法b时面,这个时候a的调用范围只能在b的作用域下面去调用,所以只能有b去调用a,这样就完成了我们的要求
第二种方案,通过控制调用者来完成
function a(){
//能否在我执行这一个方法的时候,判断一下,到底是谁调用了我呢?
//a.caller指的就是函数调用者:谁调用了我,这个地方的a.caller就是谁
//如果是全局调用它就是null
if(a.caller==b){
console.log(a.caller);
document.writeln("我是方法a");
}
}
function b(){
a();
document.writeln("我是方法b");
}
function c(){
a();
document.writeln("我是方法c");
}
说明:在上面的代码当中,我们已经在a的方法里面,做了一个if判断,使用了一个
a.caller==b
这样条件,其中a.caller
指的是谁调用了a的方法通过方法的caller这个属性,我们可以严格控制方法的调用者,也可以得到是谁调用了我
函数引用
在一个函数的内部,我们有一个属性,它会指向当前函数自身的引用,这个属性就是arguments.callee
,这个属性通常在使用递归的时候,我们会使用它