函数

一、函数简介

  JavaScript中的函数是一段代码,被定义一次,但是可以调用或执行多次。函数定义的时候会包含一个函数形参的标识符列表,这些参数在函数内部像局部变量一样工作。函数调用时会为形参提供实参的值。函数调用时还有一个值,就是本次调用的上下文,就是this关键字指向的值。

  函数是对象,我们可以把它赋值给变量或者作为函数的参数。此外,我们还可以给他设置属性和方法。

二、函数定义

  1、函数声明语句 

1 function x(a,b){
2   return a+b;
3 }

  2、函数定义表达式   

1 var x = function (a,b) {
2   return a + b;
3 }
4 //自調用函數
5 var y = (function (a, b) {
6   return a + b;
7 }(1, 2));
8 console.log(y);//3

 

  没有return语句的函数的返回值时undefined;同时函数可以嵌套在另个函数里面,内部函数可以读取外部函数的局部变量。

三、函数调用的方法

  1、作为函数

1 //函数调用
2 function X(){
3     return 0;
4 }
5 var x=X();//0

 

  函数调用的this指向全局对象,严格模式下指向undefined,如果该函数有嵌套函数,嵌套函数的this也指向全局对象(undefined)。

 

  2、方法调用 

 1 function X(){
 2     var self=this;
 3     function myfun(){
 4         console.log(self===o);//true
 5         console.log(this===global);//true
 6     }
 7     myfun();
 8     return 0;
 9 }
10 var o={};
11 o.m=X;
12 //方法调用
13 var x=o.m();//x

 

  方法调用的this指向调用方法的对象。内部函数要访问外部函数的this,需要将this保存在一个对象中。

 

  3、构造函数调用

  调用构造函数通常使用new关键字,构造函数的调用对象(this)就是这个新创建的。使用new 关键字调用构造函数时,先创建一个新的空对象,对象的原型指向构造函数的prototype属性,然后初始化这个对象并返回。

如果构造函数没有return或者有return但是返回的是一个原始值或者没有指定返回值,则表达式的值是这个新建的对象,如果return返回的是一个对象,则表达式的值是这个对象。

  4、间接调用

  使用Function.call()和Function.apply()间接调用函数,第一个参数指定调用方法的上下文(this),后面的参数指定调用方法需要的参数,call()中参数作为方法的实参,apply的参数以数组的形式传给方法的实参。

1 function x(x){
2    console.log(x);    
3 }
4 x.call(null,'hello');//hello
5 x.apply(this,['world']);//world

 

四、函数的形参和实参

  函数定义的时候传入的参数是形参,函数调用的时候传入的参数是实参,形参的引用指向实参。

  JavaScript中函数调用时如果实参多余形参或自动省略,实参少于形参时用undefined填补。

    利用不够的形参将使用undefined,我们可以实现一个参数可选的函数,可选的参数应该放在最后,当我们不给他传入值时应该使用默认值。  

1 //可选参数的函数
2 function x(a,b){
3     if(a===undefined) a=10;
4     b=b||1;
5     return a+b;
6 }
7 console.log(x()); //11

 

  arguments是指向实参对象的引用,实参对象是一个类数组对象。可以同下标的形式访问实参(第一个实参用argumens[0],...),也可以实现可变长形参的函数。

  比较任意个数的中的最大值; 

 1 //可选参数的函数
 2 function x(/*...*/){
 3    var max=arguments[0];
 4    for(var i=0;i<arguments.length;i++){
 5         if(max<arguments[i])
 6             max=arguments[i];
 7    }
 8    return max;
 9 }
10 console.log(x(1,2,3)); //3

五、实参对象callee和length属性

  callee指向当前正在执行的函数,length代表了当前调用函数的实参数量。 

1 function X(){
2     console.log(arguments.length);//0
3     function Y(){
4         console.log(arguments.caller);//Y
5     }
6     Y();
7 }
8 X();

 

六、函数的属性

  1、caller 

  如果一个函数f是在全局作用域内被调用的,则f.caller为null,相反,如果一个函数是在另外一个函数作用域内被调用的,则f.caller指向调用它的那个函数.该属性的常用形式arguments.callee.caller替代了被废弃的 arguments.caller.  

1 function X(){
2     console.log(X.caller);//null
3     function Y(){
4         console.log(Y.caller);//X
5     }
6     Y();
7 }
8 X();

  2.length属性指明函数的形参个数。 

1 function X(a,b){
2   console.log(X.length);//2
3 }
4 X();

  3、prototype属性

  每个函数都有prototype属性,该属性指向一个对象,这个对象成为原型对象。当时用该函数创建一个对象,对象会从原型对象继承属性。

  4、call()和apply()

  函数的这两个方法使的函数可以作为其他对象的方法调用。即使对象没有这个方法。

  call()和apply()的一个参数是函数调用的上下文对象,可以是null和undefined,当时null和undefined的时候,会用全局对象代替,当时原始值的时候,会转化为对应的包装对象。

  第二个参数是函数的调用的参数是要传入的参数。apply()把函数要传入的参数以数组的形式传入。

  5、bind()

  bind()用来将一个函数f绑定到一个对象o,并返回一个新的函数,这个新的函数会把f作为o的方法调用。

  bind()可以接受额外的参数,额外的实参会作为函数的参数传入; 

1 function add(x,y){return x+y};
2 var x=add.bind(null,1,2);//把函数绑定到null,x绑定1;y绑定2
3 console.log(x());//3

 

  当绑定完的返回的新函数length属性为原函数的length减去绑定的实参个数。所以上面的函数x的length为0;

   当绑定的函数作为构造函数时,函数无prototype属性,函数的this会忽略绑定的对象。创建的对象的原型指向原来函数的prototype,使用instanceof验证时,绑定函数和原函数没有区别。 

1 function X(a,b){
2     this.a=a;
3     this.b=b;
4 }
5 var o=Object.create({x:1});
6 var x=X.bind(o,1,2);//把函数绑定到o,a=1;b=2
7 var z=new x();
8 console.log(z instanceof x);//true
9 console.log(z instanceof X);//true

 

  6、toString()

  大多数函数的toString()返回源码,内置函数的toString()返回[native code].

七、函数的使用

  JavaScript中的函数是一个值,可以赋值给变量、对象的属性、数组的元素,还可以作为函数的参数。数组的sort()方法的参数就是一个函数,允许他设置数组的元素的排序规则。

  函数是一个对象,我们可以给函数定义属性。

1 //用函数的属性保存一个值,这样可以减少全局变量的使用
2 XX.n=0;
3 function XX(){
4     return XX.n++;
5 }

  作为命名空间的函数:函数内部定义的变量只在函数内部有效,而不会污染全局命名空间。模块的定义常用到这个方法,因为模块会在不同的程序中运行,我们不能保证模块中定义的变量是否在程序中已经存在,我们可以把变量定义在一个函数内部,这样变量的作用域就不是全局的。 

1 (function(){
2     //moudle中的代码
3 }())

八、闭包

  闭包:函数的变量可以隐藏于作用域链之内,因此看起来像函数把变量包裹起来了一样。

  JavaScript采用词法作用域,函数的执行依赖变量的作用域,这个作用域实在函数定义的时决定的,而不是在函数调用时决定的。为了实现这种词法作用域,JavaScript的函数不仅包含代码的逻辑部分,还要引用当前的作用域链。

 1 var scope="global scope";
 2 function f(){
 3     var scope="local scope";
 4     function fun(){
 5         return scope;
 6     }
 7     return fun;
 8 }
 9 var x=f()();
10 console.log(x);//"local scope"

  函数定义的时候会保存一个作用域链,当函数调用的时候会创建一个保存函数局部变量的对象,把对象添加到作用域链上。当函数返回时会从作用域链删除该对象。当函数内部存在嵌套函数,嵌套函数的作用域链指向该对象,如果嵌套函数在外部函数中保存下来,外部函数返回的时候变量绑定对象会被回收,但是如果嵌套函数被返回或者存储在某处的属性里,则会有一个外部引用指向该函数,他的作用域链上的绑定对象则不会被回收。当我们调用函数时会从函数的作用域上查找变量,所以上面我们访问到的还是局部变量。

  我们可以利用闭包来实现变量的私有。  

 1 function fun(){
 2     var n=10; 
 3     return {
 4         add:function f(){
 5             return  n++;
 6         },
 7         set: function g(){
 8             n=0;
 9         }
10     }
11 }
12 var o=fun();
13 o.add();//10
14 o.set
15 o.add();//0

 

  当fun()方法调用返回后,只能通过函数的嵌套函数才能访问到局部变量n,这个变量就变成了一个私有变量。两个嵌套函数(闭包)共享变量n.

  很多时候我们不希望私有变量共享。

 1 function fun(){
 2    var arr=[],i=0;
 3    for(i;i<10;i++){
 4        arr[i]=function(){
 5            return i;
 6        }
 7    }
 8    return arr;
 9 }
10 var a=fun();
11 a.forEach((ele)=>{
12     console.log(ele());//结果全是10;
13 });

  上面的代码中我们创建了10个闭包。10个闭包共享变量i; 

 1 function fun(){
 2    var arr=[],i=0;
 3    for(i;i<10;i++){
 4        arr[i]=(function(x){
 5             function f(){
 6                 return x;
 7             }
 8             return f;
 9        }(i));
10    }
11    return arr;
12 }
13 var a=fun();
14 a.forEach((ele)=>{
15     console.log(ele());//结果全是0....9;
16 });

  这个例子中我们把i作为参数传给不同的函数的形参x,然后让闭包中的x不共享。

  当使用闭包是无法访问到外部函数的this和arguments。只能将其赋值给一个变量才能访问到。

九、Function构造函数

1 var z=new Function('x','y',"return x+y");

  上面的函数等价于var z=function(x,y){return x+y}; 

posted @ 2018-03-14 00:45  Skd一路花开  阅读(249)  评论(0编辑  收藏  举报