js函数、函数参数全解(上)

  • 2
  • 21 views
  • A+
所属分类:JS、JQuery 编程技术

函数参数
arguments

javascript中的函数定义并未指定函数形参的类型,函数调用也未对传入的实参值做任何类型检查。实际上,javascript函数调用甚至不检查传入形参的个数。

1
2
3
function funcname([arg1 [,arg2 [...,argn]]]){
    statement;
}

同名形参

在非严格模式下,函数中可以出现同名形参,且只能访问最后出现的该名称的形参。

1
2
3
4
5
6
7
var functionName = function([arg1 [,arg2 [...,argn]]]){
    statement;
}
 
var functionName = function funcName([arg1 [,arg2 [...,argn]]]){
    statement;
}

而在严格模式下,出现同名形参会抛出语法错误

1
var tensquared = (function(x) {return x*x;}(10));   //定义同时进行调用

参数个数

当实参比函数声明指定的形参个数要少,剩下的形参都将设置为undefined值

1
2
3
4
5
6
var test = function fn(){
   return fn;
}
console.log(test);//fn(){return fn;}
console.log(test());//fn(){return fn;}
console.log(test()());//fn(){return fn;}

常常使用逻辑或运算符给省略的参数设置一个合理的默认值

1
2
3
4
5
var test = function fn(){
   return fn === test;
}
console.log(test());//true
console.log(test === fn);//ReferenceError: fn is not defined

[注意]实际上,使用y || 2是不严谨的,显式地设置假值(undefined、null、false、0、-0、”、NaN)也会得到相同的结果。所以应该根据实际场景进行合理设置

 

当实参比形参个数要多时,剩下的实参没有办法直接获得,需要使用即将提到的arguments对象

javascript中的参数在内部用一个数组表示。函数接收到的始终都是这个数组,而不关心数组中包含哪些参数。在函数体内可以通过arguments对象来访问这个参数数组,从而获取传递给函数的每一个参数。arguments对象并不是Array的实例,它是一个类数组对象,可以使用方括号语法访问它的每一个元素

1
2
3
4
5
6
7
8
//IE11-浏览器无效,均输出undefined
//chrome在处理匿名函数的name属性时有问题,会显示函数表达式的名字
function fn(){};
console.log(fn.name);//'fn'
var fn = function(){};
console.log(fn.name);//'',在chrome浏览器中会显示'fn'
var fn = function abc(){};
console.log(fn.name);//'abc'

 

arguments对象的length属性显示实参的个数,函数的length属性显示形参的个数

1
var functionName = new Function(['arg1' [,'arg2' [...,'argn']]],'statement;');

 

形参只是提供便利,但不是必需的

1
2
3
4
5
var sum = new Function('num1','num2','return num1 + num2');
//等价于
var sum = function(num1,num2){
    return num1+num2;
}

对象参数

当一个函数包含超过3个形参时,要记住调用函数中实参的正确顺序实在让人头疼

1
2
3
4
5
6
var test = 0;
function fn(){
    var test = 1;
    return new Function('return test');
}
console.log(fn()());//0

 

通过名/值对的形式来传入参数,这样参数的顺序就无关紧要了。定义函数的时候,传入的实参都写入一个单独的对象之中,在调用的时候传入一个对象,对象中的名/值对是真正需要的实参数据

1
var o = new Math.min();//Uncaught TypeError: Math.min is not a constructor

以函数为参数

 

函数本身是一个对象,因此可以将函数作为另一个函数的参数,进而实现函数回调,功能等同于c++中的函数指针。

1
2
3
4
//变量的重复声明无用
var a = 1;
var a;
console.log(a);//1

同步

当形参与实参的个数相同时,arguments对象的值和对应形参的值保持同步

1
2
3
4
5
6
//覆盖同名变量
var a;
function a(){
    console.log(1);
}
a();//1

[注意]虽然命名参数和对应arguments对象的值相同,但并不是相同的命名空间。它们的命名空间是独立的,但值是同步的

但在严格模式下,arguments对象的值和形参的值是独立的

1
2
3
4
5
6
7
8
//覆盖同名函数
a();//2
function a(){
    console.log(1);
}
function a(){
    console.log(2);
}

当形参并没有对应的实参时,arguments对象的值与形参的值并不对应

1
2
3
4
5
function foo(){
    console.log(1);
}
delete foo;//false
console.log(foo());//1

内部属性

【callee】

arguments对象有一个名为callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数

下面是经典的阶乘函数

1
2
3
4
5
6
7
8
9
10
function testFinnally(){
    try{
        return 2;
    }catch(error){
        return 1;
    }finally{
        return 0;
    }
}
testFinnally();//0

但是,上面这个函数的执行与函数名紧紧耦合在了一起,可以使用arguments.callee可以消除函数解耦

1
2
3
4
5
6
7
function fn(){
    this.a = 2;
    return 1;
}
var test = new fn();
console.log(test);//{a:2}
console.log(test.constructor);//fn(){this.a = 2;return 1;}

但在严格模式下,访问这个属性会抛出TypeError错误

1
2
3
4
5
6
7
8
function fn(){
    this.a = 2;
    return {a:1};
}
var test = new fn();
console.log(test);//{a:1}
console.log(test.constructor);//Object() { [native code] }

这时,可以使用具名的函数表达式

1
2
3
4
5
function add(x,y){
    return x+y;
}
var sum = add(3,4);
console.log(sum)//7

【caller】

实际上有两个caller属性

 

【1】函数的caller

函数的caller属性保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,它的值是null

1
2
3
4
function add(x,y){
    console.log(this);//window
}    
add();
1
2
3
4
5
function add(x,y){
    'use strict';
    console.log(this);//undefined
}    
add();//window

在严格模式下,访问这个属性会抛出TypeError错误

1
var strict = (function(){return !this;}());

【2】arguments对象的caller

该属性始终是undefined,定义这个属性是为了分清arguments.caller和函数的caller属性

1
2
3
4
5
6
var a = 0;
function fn(){
    this.a = 1;
}
fn();
console.log(this,this.a,a);//window 1 1

同样地,在严格模式下,访问这个属性会抛出TypeError错误

1
2
3
4
5
6
var o = {
    m: function(){
        console.log(1);
    }
};
o.m();//1

函数重载

javascript函数不能像传统意义上那样实现重载。而在其他语言中,可以为一个函数编写两个定义,只要这两个定义的签名(接受的参数的类型和数量)不同即可

javascript函数没有签名,因为其参数是由包含0或多个值的数组来表示的。而没有函数签名,真正的重载是不可能做到的

1
2
3
4
5
6
7
8
9
10
11
12
var o = {
    a: 1,
    m: function(){
        return this;
    },
    n: function(){
        this.a = 2;
    }
};
console.log(o.m().a);//1
o.n();
console.log(o.m().a);//2

只能通过检查传入函数中参数的类型和数量并作出不同的反应,来模仿方法的重载

1
2
rect.setSize(width,height);
setRectSize(rect,width,height);

参数传递

javascript中所有函数的参数都是按值传递的。也就是说,把函数外部的值复制到函数内部的参数,就和把值从一个变量复制到另一个变量一样

【1】基本类型值

在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量(命名参数或arguments对象的一个元素)

1
2
3
4
5
6
7
8
9
var o = {
    m: function(){
         function n(){
             return this;
         }
         return n();
    }
}
console.log(o.m());//window

【2】引用类型值

 

在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部

1
2
3
4
5
6
7
8
9
10
var o = {
    m: function(){
         function n(){
             'use strict';
             return this;
         }
         return n();
    }
}
console.log(o.m());//undefined

当在函数内部重写引用类型的形参时,这个变量引用的就是一个局部对象了。而这个局部对象会在函数执行完毕后立即被销毁

1
2
3
4
5
6
7
8
9
10
11
12
13
var o = {
    m: function(){
        var self = this;
        console.log(this === o);//true
         function n(){
             console.log(this === o);//false
             console.log(self === o);//true
             return self;
         }
         return n();
    }
}
console.log(o.m() === o);//true
  • 我的微信
  • 这是我的微信扫一扫
  • weinxin
  • 我的微信公众号
  • 我的微信公众号扫一扫
  • weinxin

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

目前评论:2   其中:访客  2   博主  0

    • avatar 小猴子 4

      666牛逼

      • avatar 小猴子 4

        好像很厉害的样子