JavaScript学习笔记(六)——函数

函数

1、函数定义

总共有三种函数定义的方式:函数声明语句、函数表达式、内置构造函数。

  1. 函数声明语句

function functionName(parameters) {
    //执行的代码
}

函数声明后不会立即执行,会在我们需要的时候调用到。

小练习:定义一个求阶乘的函数。

function fn2(n) {
            var s = n;
            var result = 1;
            for (var n; n >= 1; n--) {
                result *= n;
            }
            console.log(s + '的阶乘等于' + result);
        }
        fn2(12);

 

2.函数表达式

var functionName  = function (parameters) {
    //执行的代码
};
//函数以分号结尾,因为它实际上是一个执行语句

以上函数实际上是一个 匿名函数 (函数没有名称),函数存储于变量中,故通常不加函数名。

当写递归函数时,也可加上函数名。

定义一个表达式函数:

var square = function factorial(h) {
    if(h===1) {return 1;}
    else     {return h*factorial(h-1);}
}
console.log(square(3));

3.Function内置构造函数

在以上实例中,我们了解到函数通过关键字 function 定义。

函数同样可以通过内置的 JavaScript 函数构造器(Function())定义。

var functionName  = new Function("parameters","执行的代码")
//注意引号不可省

这种方式不推荐,无法写递归。

用Function()构造函数创建一个函数时并不遵循典型的作用域,它一直把它当作是顶级函数来执行。所以,在 JavaScript 中,很多时候,你需要避免使用 new 关键字。

var y = "global";
function constructFunction() {
  var y = "local";
  function test(){};
  return new Function("return y"); // 无法获取局部变量,作用域始终是全局作用域 
}
alert(  constructFunction()()   ); 

补充:函数可以嵌套在其他函数里面,也就是在函数里面可以定义函数

function distance (x1, y1, x2, y2) {
    var n = 10;
    function square(h) { console.log(n);return h*h;}
    return Math.sqrt(square(x2-x1) + square(y2-y1)).toFixed(2);
}

被嵌套的函数可以访问嵌套他们的函数的变量或参数。

2、 函数调用

javascript一共有4种调用模式:函数调用模式、方法调用模式、构造器调用模式和间接调用模式。

每种方式的不同在于 this 的初始化。

  • 函数调用模式

    var myfunc = function(a,b){
        console.log(this);
        return a+b;
    }
    ​
    alert( myfunc(3,4) );

    函数调用模式中:

    a, this是指向Window的 

    b, 返回值是由return语句决定的,如果没有return则表示没有返回值

  • 方法调用模式

    先定义一个对象,然后在对象的属性中定义方法,通过myobject.property来执行方法。

    var name = "james";
    var obj = {
      name : "wade",
      fn1 : function () {
        console.log(this.name);
      }  
    };
    obj.fn1(); //wade

    方法调用模式中:

    a, this 是指向调用该方法的对象

    b, 返回值还是由return语句决定,如果没有return表示没有返回值

  • 构造器调用模式

    如果函数或者方法调用之前带有关键字new,它就当成构造函数调用。

    function Fn () {
        this.name = "james";
        this.age = 32;
        console.log(this);   
        
    };
    var fn1 = new Fn(); //Fn整个对象 
    console.log(fn1);    //Fn整个对象 

    通过上面的代码结果分析,会得到以下结论(构造函数调用模式中):

    a, this是指向构造函数的实例

    b, 如果没有添加返回值的话,默认的返回值是this

    但是如果手动添加返回值之后呢?

    function Fn1 () {
       this.name = "james";
       return "wade" ;          
    };
    var fn1 = new Fn1();
    console.log(fn1);           
    console.log(fn1.name);  
    ​
    function Fn2 () {
       this.name = "james";
       return [1,2,3];          
    };
    var fn2 = new Fn2();    
    console.log(fn2);           
    console.log(fn2.name);  

    而通过上面的代码结果分析,优化上面的结论:

    a, this是指向构造函数的实例

    b, 如果没有添加返回值的话,默认的返回值是this

    c, 如果有返回值,且返回值是简单数据类型(Number,String,Boolean··)的话,最后仍回返回this

    d, 如果有返回值,且返回值是复杂数据类型(对象)的话,最终返回该对象,所以上面的fn2是指向数组,所以fn2.name为undefined

  • 间接调用模式

    也称之为“apply、call调用模式” 或 “上下文调用模式”。

    var myobject={};
    var sum = function(a,b){
        console.log(this);
      return a+b;
    };
    var sum2 = sum.call(myobject,10,30); 
    ​
    alert(sum2);

    由之前所学,this指向由传入的第一个参数决定。

    再看看,下面函数调用中this指向如何呢?

    function f1(){
       console.log(this);
    }
    f1.call(null);          
    f1.call(undefined);     
    f1.call(123);          
    f1.call("abc");        
    f1.call(true);         
    f1.call([1,2,3]);      

    通过上面的代码结果分析,得出以下结论(上下文调用模式中):

    a, 传递的参数不同,this的指向不同,this会指向传入参数的数据类型

    b, 返回值是由return决定,如果没有return表示没有返回值。

3、函数参数

函数可以传进来任意多个参数,无论传进来的参数是什么类型,甚至可以不传参。

function add(x){
    return x+1;
}
console.log(add());//NaN

特殊情况1:同名形参

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

function add(x,x,x){
    return x;
}
console.log( add(1,2,3) );//3
​

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

特殊情况2:参数个数与传入的实参个数不相等时

当实参比函数声明指定的形参个数要少,剩下的形参都将设置为undefined值。当实参多于形参,则只使用有效的实参,多出部分没影响。

function add(x,y){
    console.log(x,y);
}
add(1);//1  undefined

4、参数分类

总的来说,函数参数分为两类:函数显式参数(Parameters)与隐式参数(Arguments)

显式参数(Parameters)

function functionName(parameter1, parameter2, parameter3) {
  // 要执行的代码……
}

函数显式参数在函数定义时列出(即形参)。

函数调用未传参时,参数会默认设置为: undefined。有时这是可以接受的,但是建议最好为参数设置一个默认值

function myFunction(x, y) {
  if (y === undefined) {
    y = 0;
  } 
  return x+y;
}
console.log(myFunction(1)); //打印1  因为设置了当之传入一个参数,也就是y为undefined的时候默认值为0,所以返回1+0=1

或者,更简单的方式:

function myFunction(x, y) {
  y = y || 0;//  ||当y存在值时就为那个值,否则默认为0,这个方法在后面将大量用到
}

隐式参数(Arguments)

JavaScript 函数有个内置的对象 arguments 对象。

argument 对象包含了函数调用的参数数组(实参数组)。

通过这种方式你可以很方便的找到最大的一个参数的值:

x = findMax(1, 123, 500, 115, 44, 88);
​
function findMax() {
    var  max = arguments[0];
    
    if(arguments.length < 2) return max;
 
    for (var i = 0; i < arguments.length; i++) {
        if (arguments[i] > max) {
            max = arguments[i];
        }
        // max=arguments[i]>max?arguments[i]:max
    }
    return max;
}
 console.log(x);
//返回传入参数的最大值500
​
​

同理我们也可以的到传入参数的累计和:

        x = findMax(1, 123, 500, 115, 44, 88);
​
        function findMax() {
            var sum = 0;
​
            for (var i = 0; i < arguments.length; i++) {
                sum += arguments[i];
            }
            return sum;
        }
        console.log(x);//所有参数的值相加为871
  • arguments对象与传入参数的映射规则:

function sum(a,b){
​
  arguments[1]=4;  //把4赋值给第二个参数也就是b
  
  console.log(arguments[1]);
  console.log(b);
}
sum(1);
sum(1,2);
//依次打印 4 undefined 4 4
​

arguments对象与形参是相互独立的,但又存在映射规则:

  1. 当传入参数与形参个数相等时,arguments对象与形参才是一一对应的;

  2. 当传入参数与形参个数不等时,arguments对象与有传入参数的形参才存在映射规则。

     

练习一道阿里巴巴2013年的一道笔试题:

下面代码中console.log的结果是[1,2,3,4,5]的选项是()AC
//A
function foo(x){
    console.log(arguments)
    return x;
}
​
foo(1,2,3,4,5)
​
//B
function foo(x){
    console.log(arguments)
    return x;
}(1,2,3,4,5)  //无输出
​
//C
(function foo(x){
    console.log(arguments)
    return x;
})(1,2,3,4,5)
​
//D
function foo(){
    bar.apply(null,arguments);
}
function bar(x){
    console.log(arguments);
}
foo(1,2,3,4,5)

 

posted @ 2022-06-15 16:01  小狐狸ya  阅读(46)  评论(0编辑  收藏  举报