js函数

作者: __Qiao | 来源:发表于2016-08-05 22:42 被阅读0次

一、函数声明和函数表达式有什么区别?

  • 函数声明和函数表达式的区别:

    • 函数声明可以提升到其他代码之前(即函数声明前置),但函数表达式不能提升到其他代码之前
    • 函数声明必须给定函数名称,函数表达式则可以忽略函数名称
    • 函数表达式后面有分号,函数声明没有
  • 函数声明:

    getsum(1, 2);    //3
    function getsum(num1, num2){
        return num1+num2;
    }
    

等价于

  function getsum(num1, num2){
      return num1+num2;
  }
  getsum(1, 2);    //3
  • 函数表达式:

    getsum(1, 2);    //Uncaught TypeError: getsum is not a function
    var getsum = function(num1, num2){
        return num1+num2;
    };
    

等价于

  var getsum;
  getsum(1, 2);    ////Uncaught TypeError: getsum is not a function
  getsum = function(num1, num2){
      return num1+num2;
  };

二、什么是变量声明前置?什么是函数声明前置?

  • 变量声明前置就是把变量的声明提升到当前作用域的最前面。如下例:

    console.log(a);  //undefined
    var a = 1;
    

    等价于

    var a;
    console.log(a);  //undefined
    a = 1;
    

下面这个例子也是输出undefined,因为输出的是main()内的局部变量
var a = 1;
function main(){
console.log(a);
var a = 2;
}
main(); //undefined
等价于

  var a;
  function main(){
      var a;
      console.log(a);
      a = 2;
  }
  a = 1;
  main();    //undefined
  • 函数声明前置就是把函数声明提升到当前的最前面(位于变量声明前置后面)

    var num1 = 1, num2 = 2;
    getsum(num1, num2);    //3
    function getsum(num1, num2){
        return num1 + num2;
    } 
    

等价于

  var num1, num2;
  function getsum(num1, num2){
      return num1 + num2;
  }
  num1 = 1;
  num2 = 2;
  getsum(num1, num2);    //3

三、arguments是什么?

arguments是一个用于存放函数所有传入参数的类数组对象。

  function main(a, b, c){
      return arguments;
  }
  main(1, 2, 3);      //[1, 2, 3]

arguments的值由函数的传入参数决定,与函数定义的参数无关

四、函数的重载怎样实现?

  • 函数的重载是指为一个函数编写多个定义,只要这两个定义的签名(接受的参数的类型和数量)不同即可。也就是定义几个名字相同的函数但参数的类型和数量不同,当函数接受到不同的参数时就按照不同的定义执行。

  • ECMAScript函数没有签名,所以ECMAScript函数不能重载,如果同时定义两个名称相同的函数,后定义的那个函数会覆盖先定义的函数

    function addnum(num1, num2){
        return num1+num2;
    }
    function addnum(num1, num2, num3){
        return num1+num2+num3;
    }
    console.log(addnum(1, 2)) ;   //NaN, 后定义的函数覆盖了先定义的函数
    console.log(addnum(1, 2, 3)) ;    //6
    
  • 可以通过arguments来实现函数重载的功能。如下:

    function addnum(num){
        var sum = 0;
        for(var i = 0; i < arguments.length; i++){
            sum += arguments[i];
        }
        return sum;
    }
    console.log(addnum(1));    //1
    console.log(addnum(1, 2));    //3
    

五、立即执行函数表达式是什么?有什么作用

  • 立即执行函数表达式(IIFE——Immediately Invoked Function Expression),是将函数的定义放在一个圆括号里,让JavaScript引擎将其理解为一个表达式,再在函数的定义后面加一个(),以达到定义函数后立即调用该函数的效果。下面两种形式在功能上是一致的:

    (function(){ /* code */ }());    //立即执行函数表达式
    (function(){ /* code */ })();    //立即执行函数表达式
    
  • 立即执行函数表达式的作用

    • 可以创建匿名函数,避免污染全局变量

    • 形成一个单独的作用域,可以封装一些外部无法读取的私有变量

      // 写法一
      var tmp = newData;
      processData(tmp);
      storeData(tmp);
      
      // 写法二
      (function (){
          var tmp = newData;
          processData(tmp);
          storeData(tmp);
      }());
      

上面代码中,写法二比写法一更好,因为完全避免了污染全局变量

六、什么是函数的作用域链

  • 作用域
    作用域就是变量和函数的可访问范围,控制着变量与函数的可见性和生命周期。在Javascript中变量的作用域有全局作用域和局部作用域。

    • 全局作用域
      变量没有在函数内声明或者声明的时候没有带var就是全局变量,拥有全局作用域,window对象的所有属性拥有全局作用域,在代码任何地方都可以访问。
    • 局部作用域
      函数内部声明并且以var修饰的变量就是局部变量,只能在函数体内使用,函数的参数虽然没有使用var但仍然是局部变量。
  • 执行环境(运行上下文)
    执行环境(execution context)定义了变量或函数有权访问的其他数据,决定了他们的各自行为。每个执行环境都有一个与之关联的变量对象(variable object,VO),执行环境中定义的所有变量和函数都会保存在这个对象中,解析器在处理数据的时候就会访问这个内部对象。
    全局执行环境是最外层的一个执行环境,在web浏览器中全局执行环境是window对象,因此所有全局变量和函数都是作为window对象的属性和方法创建的。每个函数都有自己的执行环境,当执行流进入一个函数的时候,函数的环境会被推入一个函数栈中,而在函数执行完毕后执行环境出栈并销毁,保存在其中的所有变量和函数定义随之销毁,控制权返回到之前的执行环境中,全局的执行环境在应用程序退出(浏览器关闭)才会被销毁。

  • 作用域链
    当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象(activation object)作为变量对象。活动对象在最开始时只包含一个变量,即arguments对象(这个对象在全局环境中是不存在的)。作用域链中的下一个变量对象来自包含(外部)环境,而再下一个变量对象则来自下一个包含环境。这样,一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象。

    标识符解析是沿着作用域链一级一级地搜索标识符的过程。搜索过程始终从作用域的前端开始,然后逐级地向后回溯,直至找到标识符为止(如果找不到标识符,通常会导致错误发生)

代码

1.以下代码输出什么?

    function getInfo(name, age, sex){
        console.log('name:', name);
        console.log('age:', age);
        console.log('sex:', sex);
        console.log(arguments);
        arguments[0] = 'valley';
        console.log('name:', name);
    }
    getInfo('hunger', 28, '男');  
    //name: hunger 
    //age: 28 
    //sex: 男
    //["hunger",28,"男"] 
    //name: valley
    getInfo('hunger', 28);  
    //name: hunger 
    //age: 28 
    //sex: undefined 
    //["hunger",28] 
    //name: valley
    getInfo('男');  
    //name: 男 
    //age: undefined 
    //sex: undefined 
    //["男"] 
    //name: valley

2.写一个函数,返回参数的平方和

    function sumOfSquares(){}
    sumOfSquares(2,3,4);
    sumOfSquares(1,3);

    function sumOfSquares(num1, num2){
        var sum = 0;
        for(var i = 0; i < arguments.length; i++){
            var a = arguments[i];
            sum += a*a;
        }
        return sum;
    }
    console.log(sumOfSquares(2,3,4)); // 29
    console.log(sumOfSquares(1,3));  // 10

3.如下代码的输出?为什么

    console.log(a);  // undefined,变量声明提升
    var a = 1;
    console.log(b);  // 报错,因为变量b没有声明

等价于

    var a;
    console.log(a);  // undefined
    a = 1;
    console.log(b);  // 报错,因为变量b没有声明

4.如下代码的输出?为什么

    sayName('world');  // hello world
    sayAge(10);  // 报错,因为函数表达式不会前置,执行到这里时sayAge没有指向任何函数
    function sayName(name){
        console.log('hello', name);
    }
    var sayAge = function(age){
        console.log(age);
    }

等价于

    var sayAge;
    function sayName(name){
        console.log('hello', name);
    }
    sayName('world');  // hello world
    sayAge(10);  // 报错,执行到这里时sayAge没有指向任何函数
    sayAge = function(age){
        console.log(age);
    }

5.如下代码的输出?为什么

    function fn(){}
    var fn = 3;
    console.log(fn);  // 3

等价于

    var fn;
    function fn(){}
    fn = 3;
    console.log(fn);  // 3

6.如下代码的输出?为什么

    function fn(fn2){
        console.log(fn2);
        var fn2 = 3;
        console.log(fn2);
        console.log(fn);
        function fn2(){
            console.log('fnnn2');
        }
    }
    fn(10);
    // function
    // 3
   // function

等价于

    function fn(fn2){
        var fn2;
        function fn2(){
            console.log('fnnn2');
        }
        console.log(fn2);  
        fn2 = 3;
        console.log(fn2);  
        console.log(fn);  
    }
    fn(10);
    // function 
    // 3
   // function

7.如下代码输出什么?为什么

    var fn = 1;
    function fn(fn){
        console.log(fn);
    }
    console.log(fn(fn));  // 报错,这里变量fn存放的是一个数值,没有指向函数

等价于

    var fn
    function fn(fn){
        console.log(fn);
    }
    fn = 1;
    console.log(fn(fn));  // 报错,这里变量fn存放的是一个数值,没有指向函数

8.如下代码的输出?为什么

    console.log(j);  // undefined
    console.log(i);  // undefined
    for(var i=0; i<10; i++){
        var j = 100;
    }
    console.log(i);  // 10
    console.log(j);  // 100

等价于

    var i;
    var j;
    console.log(j);  // undefined
    console.log(i);  // undefined
    for(i=0; i<10; i++){
        j = 100;
    }
    console.log(i);  // 10
    console.log(j);  // 100

变量i,j都是全局变量,只有在函数内才会存在局部变量

9.如下代码的输出?为什么

    fn();
    var i = 10;
    var fn = 20;
    console.log(i);
    function fn(){
        console.log(i);
        var i = 99;
        fn2();
        console.log(i);
        function fn2(){
            i = 100;
        }
    }

等价于

    var i;
    var fn;
    function fn(){
        var i;
        function fn2(){
            i = 100;
        }
        console.log(i);
        i = 99;
        fn2();
        console.log(i);
    }
    fn(); 
    /**
     * console.log(i);  // undefined 
     * fn2();  // 没有输出
     * console.log(i);  // 100
     */
    i = 10;
    fn = 20;
    console.log(i);  // 10

10.如下代码的输出?为什么

    var say = 0;
    (function say(n){
        console.log(n);
        if(n<3) return;
        say(n-1);
    }(10));
    /**
     *立即执行函数表达式,传入参数为10,即n=10
     *console.log(n);  // 10
     *if(n<3) return;  //n<3时跳出循环
     *say(n-1);  // 9 8 7 6 5 4 3 2
     */
    console.log(say);  // 0, 这里的say是全局变量say,立即执行函数表达式中的say是局部变量

本文版权属吴天乔所有,转载务必注明出处。

相关文章

网友评论

      本文标题:js函数

      本文链接:https://www.haomeiwen.com/subject/itqcsttx.html