JS-函数和作用域

1.函数声明和函数表达式有什么区别

函数声明和函数表达式是声明函数的两种不同的方式,形式如下:

  • 函数声明:即使用function关键字声明一个函数
 //函数声明
 function sayHello(){
   console.log('hello')
 }

 //函数调用
 sayHello()

声明不必放到调用的前面,函数调用可以发生在函数声明之前,例如下面这种情况下不会报错

  printName();//输出console.log('1')
  function printName(){
    console.log('1');
  }
  //正常,因为提升了函数声明,函数调用可在函数声明之前

即在一个作用域下,var 声明的变量和function 声明的函数会前置

  • 函数表达式
  var sayHello = function(){
    console.log('hello');
  }

  sayHello()

声明必须放到调用的前面,例如

  printName();//浏览器提示Uncaught TypeError: printName is not a function(…)
  var printName = function(){
  console.log('1');
    };
  //报错,函数表达式和 var 一个变量没什么区别,变量printName还未保存对函数的引用,函数调用必须在函数表达式之后

浏览器提示Uncaught TypeError: printName is not a function(…).原因:类似变量提升,函数作为一个变量赋值给printName,等价于

  var printName;    //此时printName为undefined
  printName();
  printName = function(){
    console.log('1');
  };
  • 因此,函数声明和函数表达式不同之处在于:
    • Javascript引擎在解析javascript代码时会‘函数声明提升’(Function declaration Hoisting)当前执行环境(作用域)上的函数声明,而函数表达式必须等到Javascirtp引擎执行到它所在行时,才会从上而下一行一行地解析函数表达式,
    • 函数声明后面可以加括号立即调用该函数,函数表达式不可以,只能以fnName()形式调用

2.什么是变量的声明前置?什么是函数的声明前置

  • 变量声明前置
    • JavaScript引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。这造成的结果,就是所有的变量的声明语句,都会被提升到当前作用域的头部,这就叫做变量的声明前置,也叫变量提升(hoisting)。
  • 函数声明前置
    • JavaScript引擎将函数名视同变量名,所以采用function命令声明函数时,函数声明会像变量声明一样,被提升到代码头部。因此,函数的调用可以写在声明前面,函数可以被执行。(注意:如下函数表达式,变量fn还未保存对函数的引用,函数调用必须在函数表达式之后)。
      console.log(fn); //undefined
      fn(); //浏览器提示Uncaught TypeError: fn is not a function(…)
    
      var fn = function(){}
      //报错,变量fn还未保存对函数的引用,函数调用必须在函数表达式之后
    

3.arguments 是什么

  • arguments是一个类数组对象,类似数组的方式,可以通过下标的方式去获取值,但它本身不是数组,没有数组的一些特性,所以叫类数组对象。在函数内部,可以使用arguments对象获取到该函数的所有传入参数。

    • 例如如下函数
      function printPersonInfo(name, age, sex){
        console.log(name);
        console.log(age);
        console.log(sex);
        console.log(arguments);
      }
    
    • 执行printPersonInfo('liu', 21, 'boy')后,输出:
      name : liu
      age : 22
      sex : boy
      ["liu", 22, "boy"]
    
    • 执行printPersonInfo('liu', 21,)后,输出:
      name : liu
      age : boy
      sex : undefined
      ["liu", "boy"]
    

4.函数的"重载"怎样实现

  • 重载是指不同的函数使用相同的函数名,但是函数的参数个数或类型不同。调用的时候根据函数的参数来区别不同的函数。
  • JS并不像其他强类型语言一样可以声明重载函数,若在原先声明的函数后再声明一个不同参数数量的函数(JS是弱语言,声明的函数不需要指明参数类型),解析器会用后面声明的函数覆盖前面声明的函数。

JS种没有重载! 同名函数会覆盖。但可以在函数体针对不同的参数调用执行相应的逻辑,如下

function printPeopleInfo(name, age, sex){
    if(name){
      console.log(name);
    }

    if(age){
      console.log(age);
    }

    if(sex){
      console.log(sex);
    }
  }
  printPeopleInfo('Byron', 26);
  printPeopleInfo('Byron', 26, 'male');

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

  (function(){ var a=1; })();
  (function(){ var a=1; }());  
  • 作用:

    • 作用:隔离作用域。
  • 参考

6.递归

  • 递归实现一个函数,计算 n!
  function factor(n) {
    if(n===1){
      return n
    }
    return n*factor(n-1)
  }

几个代码小题目

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('饥人谷', 2, '男');
  getInfo('小谷', 3);
  getInfo('男');
  • getInfo('饥人谷', 2, '男');输出:
  name:饥人谷
  age:2
  sex:男
  ["饥人谷", 2, "男"]
  name valley
  • getInfo('小谷', 3);输出:
  name:小谷
  age:3
  sex:undefined
  ["小谷", 3]
  name valley
  • getInfo('男');输出:
  name:男
  age:undefined
  sex:undefined
  ["男"]
  name valley

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

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

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

  console.log(a);//undefined
  var a = 1;
  console.log(b);//报错Uncaught ReferenceError: b is not defined(…)

a输出undefined是因为变量提升,b没有定义,类似如下代码

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

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

  sayName('world');   //输出 'hello ', 'world' 使用函数声明做出的声明前置
  sayAge(10);        //报错 Uncaught TypeError: sayAge is not a function(…) sayAge此时还为被赋值为函数
  function sayName(name){
    console.log('hello ', name);
  }
  var sayAge = function(age){
    console.log(age);
  };

作用域链

5.如下代码输出什么? 写出作用域链查找过程伪代码

// 输出10

  var x = 10
  bar()       //输出10
  function foo() {
    console.log(x)
  }
  function bar(){
    var x = 30
    foo()
  }

作用域链查找过程伪代码

/*
  1.
  globalContext = {
    AO:{
      x: 10
      foo:funcation(){}
      bar:funcation(){}
    }
    Scope: null
  }

  foo.[[scope]] = globalContext.AO
  bar.[[scope]] = globalContext.AO

  2.
  barContext = {
    AO:{
      x: 30
    }
    scope: bar.[[scope]] // globalContext.AO
  }
  3.
  fooContext = {
    AO:{}
    scope: foo.[[scope]] // globalContext.AO
  }

*/

6.如下代码输出什么? 写出作用域链查找过程伪代码

// 输出30

  var x = 10;
  bar()     //输出30
  function bar(){
    var x = 30;
    function foo(){
      console.log(x) 
    }
    foo();
  } 

作用域链查找过程伪代码

/*
  1.
  globalContext = {
    AO:{
      x: 10
      bar:funcation(){}
    }
    Scope: null
  }
  bar.[[scope]] = globalContext.AO

  2.
  barContext = {
    AO:{
      x: 30
      foo:funcation(){}
    }
    Scope: bar.[[scope]] // globalContext.AO
  }
  foo.[[scope]] = barContext.AO

  3.
  fooContext = {
    AO:{}
    Scope: foo.[[scope]] // barContext.AO
  }
*/

7.以下代码输出什么? 写出作用域链的查找过程伪代码

输出30

  var x = 10;
  bar() 
  function bar(){
    var x = 30;
    (function (){
      console.log(x)    // 30
    })()
  }
/*
  globalContext = {
    AO:{
      x: 10
      bar:funcation(){}
    }
    Scope: null
  }
  bar.[[scope]] // globalContext.AO

  2.
  barContext = {
    AO:{
      x: 30
      匿名函数A:funcation(){}
    }
    Scope: bar.[[scope]] = globalContext.AO
  }
  匿名函数A.[[scope]] // barContext.AO

  3.
  匿名函数AContext = {
    AO:{}
    Scope: 匿名函数A.[[scope]] // barContext.AO
  }
*/  

8.以下代码输出什么? 写出作用域链查找过程伪代码

  var a = 1;

  function fn(){
    console.log(a)
    var a = 5
    console.log(a)
    a++
    var a
    fn3()
    fn2()
    console.log(a)

    function fn2(){
      console.log(a)
      a = 20
    }
  }

  function fn3(){
    console.log(a)
    a = 200
  }

  fn()                //输出: undefined 5 1 6 20 
  console.log(a)      //输出: 200
/*
1.
  globalContext = {
    AO:{
      a: 1 -> 200
      fn:funcation(){}
      fn3:funcation(){}
    }
    Scope: null
  }
  fn.[[scope]] = globalContext.AO
  fn3.[[scope]] = globalContext.AO

  2.
  fnContext = {
    AO:{
      a: 5 -> 6 -> 20
      fn2:funcation(){}
    }
    Scope: fn.[[scope]] // globalContext.AO
  }
  fn2.[[scope]] = fnContext.AO

  3.
  fn3Context = {
    AO:{}
    Scope: foo.[[scope]] // globalContext.AO
  }

  4.
  fn2Context = {
    A:{}
    Scope: fn2.[[scope]] // fnContext.AO
  }
*/
注意:在JavaScript里,每个函数,当被调用时,都会创建一个新的执行上下文。因为在函数里定义的变量和函数只能在函数内部被访问,外部无法获取;当调用函数时,函数提供的上下文就提供了一个非常简单的方法创建私有变量。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 一、函数声明和函数表达式 函数声明的形式:function fn() { } ,使用函数声明的形式声明一个函数时,...
    sutingy阅读 1,209评论 0 0
  • 一、函数声明function(){}是function +函数名字(){内容}调用函数是 函数名字();funct...
    崔敏嫣阅读 2,241评论 0 0
  • 函数声明和函数表达式 函数声明: fuction fn(){console.log("test");} 函数表达式...
    赵BW阅读 2,405评论 0 0
  • 函数声明和函数表达式有什么区别 用函数声明创建的函数可以在定义之前就进行调用(声明前置);而用函数表达式创建的函数...
    727上上上阅读 775评论 0 0
  • 1.函数声明和函数表达式有什么区别 (*) 区别: 函数声明后面的分号可加可不加,不加也不影响接下来语句的执行,但...
    Sheldon_Yee阅读 3,068评论 0 1