JS-函數(shù)和作用域

1.函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別

函數(shù)聲明和函數(shù)表達(dá)式是聲明函數(shù)的兩種不同的方式,形式如下:

  • 函數(shù)聲明:即使用function關(guān)鍵字聲明一個(gè)函數(shù)
 //函數(shù)聲明
 function sayHello(){
   console.log('hello')
 }

 //函數(shù)調(diào)用
 sayHello()

聲明不必放到調(diào)用的前面,函數(shù)調(diào)用可以發(fā)生在函數(shù)聲明之前,例如下面這種情況下不會(huì)報(bào)錯(cuò)

  printName();//輸出console.log('1')
  function printName(){
    console.log('1');
  }
  //正常,因?yàn)樘嵘撕瘮?shù)聲明,函數(shù)調(diào)用可在函數(shù)聲明之前

即在一個(gè)作用域下,var 聲明的變量和function 聲明的函數(shù)會(huì)前置

  • 函數(shù)表達(dá)式
  var sayHello = function(){
    console.log('hello');
  }

  sayHello()

聲明必須放到調(diào)用的前面,例如

  printName();//瀏覽器提示Uncaught TypeError: printName is not a function(…)
  var printName = function(){
  console.log('1');
    };
  //報(bào)錯(cuò),函數(shù)表達(dá)式和 var 一個(gè)變量沒(méi)什么區(qū)別,變量printName還未保存對(duì)函數(shù)的引用,函數(shù)調(diào)用必須在函數(shù)表達(dá)式之后

瀏覽器提示Uncaught TypeError: printName is not a function(…).原因:類似變量提升,函數(shù)作為一個(gè)變量賦值給printName,等價(jià)于

  var printName;    //此時(shí)printName為undefined
  printName();
  printName = function(){
    console.log('1');
  };
  • 因此,函數(shù)聲明和函數(shù)表達(dá)式不同之處在于:
    • Javascript引擎在解析javascript代碼時(shí)會(huì)‘函數(shù)聲明提升’(Function declaration Hoisting)當(dāng)前執(zhí)行環(huán)境(作用域)上的函數(shù)聲明,而函數(shù)表達(dá)式必須等到Javascirtp引擎執(zhí)行到它所在行時(shí),才會(huì)從上而下一行一行地解析函數(shù)表達(dá)式,
    • 函數(shù)聲明后面可以加括號(hào)立即調(diào)用該函數(shù),函數(shù)表達(dá)式不可以,只能以fnName()形式調(diào)用

2.什么是變量的聲明前置?什么是函數(shù)的聲明前置

  • 變量聲明前置
    • JavaScript引擎的工作方式是,先解析代碼,獲取所有被聲明的變量,然后再一行一行地運(yùn)行。這造成的結(jié)果,就是所有的變量的聲明語(yǔ)句,都會(huì)被提升到當(dāng)前作用域的頭部,這就叫做變量的聲明前置,也叫變量提升(hoisting)。
  • 函數(shù)聲明前置
    • JavaScript引擎將函數(shù)名視同變量名,所以采用function命令聲明函數(shù)時(shí),函數(shù)聲明會(huì)像變量聲明一樣,被提升到代碼頭部。因此,函數(shù)的調(diào)用可以寫(xiě)在聲明前面,函數(shù)可以被執(zhí)行。(注意:如下函數(shù)表達(dá)式,變量fn還未保存對(duì)函數(shù)的引用,函數(shù)調(diào)用必須在函數(shù)表達(dá)式之后)。
      console.log(fn); //undefined
      fn(); //瀏覽器提示Uncaught TypeError: fn is not a function(…)
    
      var fn = function(){}
      //報(bào)錯(cuò),變量fn還未保存對(duì)函數(shù)的引用,函數(shù)調(diào)用必須在函數(shù)表達(dá)式之后
    

3.arguments 是什么

  • arguments是一個(gè)類數(shù)組對(duì)象,類似數(shù)組的方式,可以通過(guò)下標(biāo)的方式去獲取值,但它本身不是數(shù)組,沒(méi)有數(shù)組的一些特性,所以叫類數(shù)組對(duì)象。在函數(shù)內(nèi)部,可以使用arguments對(duì)象獲取到該函數(shù)的所有傳入?yún)?shù)。

    • 例如如下函數(shù)
      function printPersonInfo(name, age, sex){
        console.log(name);
        console.log(age);
        console.log(sex);
        console.log(arguments);
      }
    
    • 執(zhí)行printPersonInfo('liu', 21, 'boy')后,輸出:
      name : liu
      age : 22
      sex : boy
      ["liu", 22, "boy"]
    
    • 執(zhí)行printPersonInfo('liu', 21,)后,輸出:
      name : liu
      age : boy
      sex : undefined
      ["liu", "boy"]
    

4.函數(shù)的"重載"怎樣實(shí)現(xiàn)

  • 重載是指不同的函數(shù)使用相同的函數(shù)名,但是函數(shù)的參數(shù)個(gè)數(shù)或類型不同。調(diào)用的時(shí)候根據(jù)函數(shù)的參數(shù)來(lái)區(qū)別不同的函數(shù)。
  • JS并不像其他強(qiáng)類型語(yǔ)言一樣可以聲明重載函數(shù),若在原先聲明的函數(shù)后再聲明一個(gè)不同參數(shù)數(shù)量的函數(shù)(JS是弱語(yǔ)言,聲明的函數(shù)不需要指明參數(shù)類型),解析器會(huì)用后面聲明的函數(shù)覆蓋前面聲明的函數(shù)。

JS種沒(méi)有重載! 同名函數(shù)會(huì)覆蓋。但可以在函數(shù)體針對(duì)不同的參數(shù)調(diào)用執(zhí)行相應(yīng)的邏輯,如下

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. 立即執(zhí)行函數(shù)表達(dá)式是什么?有什么作用

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

    • 作用:隔離作用域。
  • 參考

6.遞歸

  • 遞歸實(shí)現(xiàn)一個(gè)函數(shù),計(jì)算 n!
  function factor(n) {
    if(n===1){
      return n
    }
    return n*factor(n-1)
  }

幾個(gè)代碼小題目

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.寫(xiě)一個(gè)函數(shù),返回參數(shù)的平方和

 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);//報(bào)錯(cuò)Uncaught ReferenceError: b is not defined(…)

a輸出undefined是因?yàn)樽兞刻嵘?b沒(méi)有定義,類似如下代碼

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

4.如下代碼的輸出?為什么

  sayName('world');   //輸出 'hello ', 'world' 使用函數(shù)聲明做出的聲明前置
  sayAge(10);        //報(bào)錯(cuò) Uncaught TypeError: sayAge is not a function(…) sayAge此時(shí)還為被賦值為函數(shù)
  function sayName(name){
    console.log('hello ', name);
  }
  var sayAge = function(age){
    console.log(age);
  };

作用域鏈

5.如下代碼輸出什么? 寫(xiě)出作用域鏈查找過(guò)程偽代碼

// 輸出10

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

作用域鏈查找過(guò)程偽代碼

/*
  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.如下代碼輸出什么? 寫(xiě)出作用域鏈查找過(guò)程偽代碼

// 輸出30

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

作用域鏈查找過(guò)程偽代碼

/*
  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.以下代碼輸出什么? 寫(xiě)出作用域鏈的查找過(guò)程偽代碼

輸出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
      匿名函數(shù)A:funcation(){}
    }
    Scope: bar.[[scope]] = globalContext.AO
  }
  匿名函數(shù)A.[[scope]] // barContext.AO

  3.
  匿名函數(shù)AContext = {
    AO:{}
    Scope: 匿名函數(shù)A.[[scope]] // barContext.AO
  }
*/  

8.以下代碼輸出什么? 寫(xiě)出作用域鏈查找過(guò)程偽代碼

  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里,每個(gè)函數(shù),當(dāng)被調(diào)用時(shí),都會(huì)創(chuàng)建一個(gè)新的執(zhí)行上下文。因?yàn)樵诤瘮?shù)里定義的變量和函數(shù)只能在函數(shù)內(nèi)部被訪問(wèn),外部無(wú)法獲?。划?dāng)調(diào)用函數(shù)時(shí),函數(shù)提供的上下文就提供了一個(gè)非常簡(jiǎn)單的方法創(chuàng)建私有變量。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、函數(shù)聲明和函數(shù)表達(dá)式 函數(shù)聲明的形式:function fn() { } ,使用函數(shù)聲明的形式聲明一個(gè)函數(shù)時(shí),...
    sutingy閱讀 208評(píng)論 0 0
  • 一、函數(shù)聲明function(){}是function +函數(shù)名字(){內(nèi)容}調(diào)用函數(shù)是 函數(shù)名字();funct...
    崔敏嫣閱讀 333評(píng)論 0 0
  • 函數(shù)聲明和函數(shù)表達(dá)式 函數(shù)聲明: fuction fn(){console.log("test");} 函數(shù)表達(dá)式...
    趙BW閱讀 334評(píng)論 0 0
  • 函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別 用函數(shù)聲明創(chuàng)建的函數(shù)可以在定義之前就進(jìn)行調(diào)用(聲明前置);而用函數(shù)表達(dá)式創(chuàng)建的函數(shù)...
    727上上上閱讀 101評(píng)論 0 0
  • 1.函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別 (*) 區(qū)別: 函數(shù)聲明后面的分號(hào)可加可不加,不加也不影響接下來(lái)語(yǔ)句的執(zhí)行,但...
    Sheldon_Yee閱讀 469評(píng)論 0 1

友情鏈接更多精彩內(nèi)容