方法的定义很常见,由两种一个是常规写法,还有要给是函数表达式。
function test(){
#执行程序
}
#函数表达式
var test1=function(){
#执行程序
}
形参
JavaScript方法自然也可以进行参数,也就是在方法中先来一个形参进行占位。
function test(a,b){
# 执行程序
}
js中形参,不像是java中那样形参中直接定义其必须传输的数据类型。
test(1,2)
test("a","b")
调用都可以运行,甚至参数可以是方法
方法的length属性
方法的length属性得到的是形参的个数。
JavaScript中的参数传递,可以与形参的个数不同,比如下面:
function test(a,b){
console.log(a+" "+b)
}
test(1)# 输出的是 1 undefined
test(1,3,4)# 输出的是 1 3
test(b=3)# 输出的是 3 undefined (为什么会这样输出,因为python中可以直接通过形参名进行赋值,二JavaScript中却不是,而是可以理解为: 创建一个变量b 然后赋值成3,然后将变量b的值传递给test第一个参数a)
方法中的arguments属性
其实方法中还有一个神奇的属性:arguments,不过其经常再方法内部使用。
function test(a,b){
console.log(arguments.length)
console.log(arguments[0],arguments[1],arguments[2])
console.log(a+" "+b)
}
test(1,2,3)
如果形参和实参不一样的话,可以看出:arguments属性得到的是实参的数据,而不是形参。 所以可以进行下面的一个实例:一个方法计算无论多少个数字参数的的和。
function add(){
sum=0;
for (i in add.arguments) {
sum+=arguments[i];
}
return sum;
}
# 调用
test(1,3,4) # 8
test(1) # 1
不过一点arguments和形参数据一般会修改彼此的值,会影响彼此,但是有些情况下不是的,这个在下面讲。
形参的定义和赋值
讲解这个时候,需要又要扯到var这个定义变量会提升的问题,以及其会变量所在的域。先看下面这个例子:
var a=10;
function test(){
var a=12
console.log(a)
}
test()
console.log(a)
#最后输出
12
10
# 这个地方可以看出。如果使用var定义了变量,两个a的值不会相互影响。
var a=10;
function test(){
a=12
console.log(a)
}
test()
console.log(a)
#最后输出
12
12
#这个地方可以看出,当变量前面没有什么var修饰的时候,会变成全局变量,相当于 window.a=12,会将前面的var a=10的值修改掉。
var a=10
function test(){
a=12;
console.log(a);
var a=2;#
}
console.log(a);
#输出
12
10
# 这个地方是不是很神奇,因为这个没有坐别的事情,只是在方法的域中最后添加var a;
其实这个有点涉及到局部变量(AO)了,后面我们单独一篇聊全局变量BO和AO(很重要的,毕竟了解js的闭包就需要先了解AO和BO)
test()调用方法的时候,
第一步:因为没有传递参数,加载方法的时候,先预缓存的时候,方法域中遇见var a 就提高其位置,所以运行的时候
变成了:
function test(){
var a=undefined;
a=12;
console.log(a);
a=2;#
}
这个时候 方法中a变量就被提前使用var ,所方法域中的变成了局部的变量。
讲解参数为什么会说上面呢?请看下面前面聊js中arguments属性的时候,可以看出形参会变成arguments属性的的数组中,如果赋值一样的话,很容易理解。
比如修改两者的数值,都会影响彼此(这个涉及到JavaScript 中的clone后面单独聊,现在记住即可)
function test(a,b){
console.log(a,b);
console.log( arguments[0], arguments[1]);
console.log("------------------");
arguments[0]=12;
arguments[1]=13;
console.log(a,b);
console.log( arguments[0], arguments[1]);
console.log("------------------");
a=12;
b=13;
console.log(a,b);
console.log( arguments[0], arguments[1]);
}
# 输出
test(1,2)
但是涉及到一个如果传参数,和形参不一样呢?
function test(a,b){
console.log(a,b);
console.log( arguments[0], arguments[1]);
console.log("------------------");
a=66;
var b=99;
console.log(a,b);
console.log( arguments[0], arguments[1]);
arguments[0]=44;
arguments[1]=88;
console.log(a,b);
console.log( arguments[0], arguments[1]);
console.log("------------------");
}
# 其中 b前面带不带 var 这个关键字都不会影响结果(下面截图 b前面带var 演示)
test(1)
简单总结:如果形参在函数调用的时候赋值了,那就会和arguments属性中下标对应的数值相互影响否则不会。
为什么会出现这个状况:
形参和实参为什么会影响彼此呢:
- 第一:形参和实参(arguments)本质上是不同的东西。形参在栈中,而arguments其数据存在堆中,而指向数据的指针存在栈中。
- 第二:但是为什么修改彼此会影响。因为在方法这个里面,两者的数据会形参一个影响彼此的映射。如果在调用方法中形参中的一个未被赋值,那这个映射就不会形成,同意如果传递的参数个数多余形参,多余的也不会形成映射。
形参默认值
其实在JavaScript中形参可以设置默认值的,比如下面:
#这个只有在es6版本的时候才会支持。
function test(a=1,b){
console.log(a,b);
}
可以看出a的值默认是1也可以打印出来,但是又涉及一个神奇的问题
那就是如何给b传值而你修改a的值?
function test(a=1,b){
console.log(a,b);
}
test(2) #这样调用还是直接让a=2,b=undefined
test(1,2)#这个当然也可以,但是好像也没有什么意义了。如果公司很多接口说这个方法调用第一个参数用默认值,难道每次都看一下接口源码?
test(undefined,2)# a=1 b=2
#如果使用 null,NaN 也会影响a的值
前面说过,形参和实参是映射关系,但是如果用了默认值,那就是映射关系就不存在了,彼此不会影响彼此
function test(a=1,b){
console.log(a,b);
console.log( arguments[0], arguments[1]);
console.log("------------------");
a=66;
var b=99;
console.log(a,b);
console.log( arguments[0], arguments[1]);
arguments[0]=44;
arguments[1]=88;
console.log(a,b);
console.log( arguments[0], arguments[1]);
console.log("------------------");
}
两者关系不在映射影响彼此,但是形参还是可以得到实参的值,如果对应的形参和实参,其中一个是非undefined 一个是undefined的,那么方法会使用非undefined的值。
如果是es5也要又类似的用法咋办?
第一种:
function test(a,b){
#使用if 这个需要使用typeof这个方法
if (typeof(arguments[0])!=="undefined"){
a=arguments[0]
}else{
a=1
}
}
第二种:三目运算符
function test(a,b){
#使用if 这个需要使用typeof这个方法
a=typeof(arguments[0])!=="undefined")?arguments[0]:1
}
第三种:
function test(a,b){
#使用|| 这逻辑运算符 ""(空字符串) NaN null undefined 0 转换布尔都是false
a=arguments[0]|| 1;
}
补充
这种现象是突然发现的,自己还是有点没有绕明白,不过先让大家记住这种现象(为了方便记住,还是用上面例子进行对比。)
function test(a,b){
console.log(a,b);
console.log( arguments[0], arguments[1]);
console.log("------------------");
arguments[0]=12;
arguments[1]=13;
console.log(a,b);
console.log( arguments[0], arguments[1]);
console.log("------------------");
a=12;
b=13;
console.log(a,b);
console.log( arguments[0], arguments[1]);
}
# 输出
test(1,2)
function test(a,b){
console.log(a,b);
console.log( test.arguments[0], test.arguments[1]);
console.log("------------------");
test.arguments[0]=12;
test.arguments[1]=13;
console.log(a,b);
console.log( test.arguments[0], test.arguments[1]);
console.log("------------------");
a=12;
b=13;
console.log(a,b);
console.log( test.arguments[0], test.arguments[1]);
}
发现再调用arguments的时候,再方法中也可以直接调用,但是无意中用方面名进行调用。就发现一点,那就是
通过带有方法名test.arguments[0]赋予值,不会修改形参传递的值。但是修改形参的值后会影响它,所以觉得是test.arguments每会复制一个新的数组,而这个方法中直接arguments,默认是一个参数数组,而这个数组会指向形参指针所指的地址,所以看下面:
function test(a,b){
console.log(a,b);
console.log(arguments);
console.log(test.arguments);
console.log(arguments==test.arguments);
console.log(arguments===test.arguments);
console.log("------------------");
console.log(test.arguments==test.arguments);
console.log(test.arguments===test.arguments);
console.log("------------------");
console.log( arguments==arguments);
console.log(arguments===arguments);
}
#执行
test(1,3)
通过这个结果可以看出预测的还算正确,不过这个需要补充一点,目前只知其果,而本人还没有捋清楚具体的运行逻辑以及过程。
arguments本质是类数组
前面一直说其是数组,因为方便解释,但是其本质还真不是数组,而是一直类数组。不信看下面
function test(a,b){
var arr=[1,3,4];
console.log("arr的本质",typeof(arr));
console.log("arguments的本质",arguments);
}
可见两者的typeof的返回值还是不一样的,这个地方涉及到一个问题,那就是数组的typeof为什么返回的是一个Object。这个问题后面聊。
return
函数默认带有一个return
function test(){
执行程序
return;#如果不写,系统运行的时候会自动添加一个
}
所以默认调用函数,都会返回一个undefined。当然如果自己添加一个return 如果程序安装逻辑运行此命令就会终止这个方法后的程序执行。
这个先不深入讲解了,后面的闭包的时候可以在深入了解一下return的妙用。
递归
简单的说,就是自己调用自己,会让代码看着简洁,但是会消耗系统性能。这个就不再阐述,很多编程语言中都有,直接用一个方法演示。比如输入要给数字,求出1到这个值的和
function test(n){
if (n<1){
return "输出值有问题,请检查重新输入";
}
if (n==1){
return 1;
}
return n+test(n-1);
}