函數(shù)&聲明前置&作用域鏈

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

JavaScript函數(shù)是指一個特定代碼塊,可能包含多條語句,可以通過名字來供其它語句調(diào)用以執(zhí)行函數(shù)包含的代碼語句。
ECMAScript規(guī)定了三種聲明函數(shù)方式:

  • 構(gòu)造函數(shù)(不推薦):
    首先函數(shù)也是對象的一種,我們可以通過其構(gòu)造函數(shù),使用new來創(chuàng)建一個函數(shù)對象。
    var sayHello = new Function("console.log('hello world');");

  • 函數(shù)聲明:
    使用function關(guān)鍵字可以聲明一個函數(shù)。

  //函數(shù)聲明
  function sayHello(){
    console.log('hello');
  }
  //函數(shù)調(diào)用
  sayHello();
  • 函數(shù)表達式:
var sayHello = function(){
    console.log('hello');
  }
//函數(shù)調(diào)用
sayHello();

區(qū)別:

函數(shù)聲明:function functionName(){} ; //函數(shù)聲明會提前,聲明不必放到調(diào)用的前面。
函數(shù)表達式:var fn = function(){} ; //函數(shù)表達式可以省略函數(shù)名,聲明必須放到調(diào)用的前面。

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

  • 變量聲明前置
    JavaScript引擎的工作方式是,先解析代碼,獲取所有被聲明的變量,然后再一行一行地運行。這造成的結(jié)果,就是所有的變量的聲明語句,都會被提升到代碼的頭部。
    例如:
console.log(a);
var a = 3;
console.log(a); 

經(jīng)過變量的聲明前置,可以理解為:

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

注意:
var重復(fù)聲明一個已經(jīng)存在的變量,原變量值不變。
不寫var會聲明一個全局的變量,在編程中應(yīng)該要避免的,即使真的需要全局變量,也應(yīng)該在最外層作用域使用var聲明。

  • 函數(shù)聲明前置
    JavaScript引擎將函數(shù)名視同變量名,所以采用function聲明函數(shù)時,整個函數(shù)會像var聲明變量一樣,被提升到代碼頭部。所以,形如下面的代碼是不會報錯的。
sayHello();
function sayHello(){
  console.log('hello');
}

聲明前置后,即:

function sayHello(){
  console.log('hello');
}
sayHello();  //'hello'

3. arguments 是什么?

arguments是一個類數(shù)組對象。代表傳給一個function的參數(shù)列表。
可以在函數(shù)內(nèi)部通過使用 arguments 對象來獲取函數(shù)的所有參數(shù)。這個對象為傳遞給函數(shù)的每個參數(shù)建立一個條目,條目的索引號從0開始:arguments[0]就是第一個參數(shù),arguments[1]就是第二個參數(shù),以此類推。這個對象只有在函數(shù)體內(nèi)部,才可以使用。

function printPersonInfo(name, age, sex){
    //console.log(name);
    //console.log(age);
    //console.log(sex);
    console.log(arguments);
}

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

  • 重載是很多面向?qū)ο笳Z言實現(xiàn)多態(tài)的手段之一,在靜態(tài)語言中確定一個函數(shù)的手段是靠方法簽名——函數(shù)名+參數(shù)列表,也就是說相同名字的函數(shù)參數(shù)個數(shù)不同或者順序不同都被認為是不同的函數(shù),稱為函數(shù)重載。
  • 在JavaScript中沒有函數(shù)重載的概念,函數(shù)通過名字確定唯一性,參數(shù)不同也被認為是相同的函數(shù),后面的覆蓋前面的,但可以在函數(shù)體針對不同的參數(shù)調(diào)用執(zhí)行相應(yīng)的邏輯。
function userInfo(name,age,sex){
      if(name){
            console.log(name);
      }
      if(age){
            console.log(age);
      }
      if(sex){
            console.log(sex);
      }
}
userInfo("andy",22);    //參數(shù)依次傳遞值,缺少的參數(shù)返回的是undefined
userInfo("andrea",20,"female");

5. 立即執(zhí)行函數(shù)表達式是什么?有什么作用?

立即執(zhí)行函數(shù)表達式(Immediately-Invoked Function Expression),簡稱IIFE。表示定義函數(shù)之后,立即調(diào)用該函數(shù)。

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

其他寫法:

    (function fn1() {});
  • 當圓括號出現(xiàn)在匿名函數(shù)的末尾想要調(diào)用函數(shù)時,它會默認將函數(shù)當成是函數(shù)聲明。
  • 當圓括號包裹函數(shù)時,它會默認將函數(shù)作為表達式去解析,而不是函數(shù)聲明。
  • 作用:隔離作用域。

6. 求n!,用遞歸來實現(xiàn)

function factor(n){
     if(n === 1 || n === 0){
          return 1;
     }
     if(n > 1){
          return n*factor(n-1);
     }else{
          console.log('Input Error');
     }  
}
factor(5);   //120

7. 以下代碼輸出什么?

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('谷哥', 20, '女');    
getInfo('小谷', 30);
getInfo('男');

答案如下:

getInfo('谷哥', 20, '女');
       name : 谷哥
       age : 20
       sex : 女
       [name:谷哥, age:20,sex:女]
       name valley
       name valley

getInfo('小谷', 30);
       name:小谷
       age:30
       sex:undefined
       [name:小谷,age:30,sex:undefined]
       name valley
       name valley

getInfo('男');
       name:男
       age:undefined
       sex:undefined
       [name:男,age:undefined,sex:undefined]
       name valley
       name valley

8. 寫一個函數(shù),返回參數(shù)的平方和

function sumOfSquares(){
      var sum = 0;
      for(var i = 0 ; i < arguments.length ; i++){
           sum = 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

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

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

變量聲明前置后,即:

var a
console.log(a) ; //undefined
a = 1
console.log(b) ; // b is not defined 沒有聲明變量b

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

sayName('world');
sayAge(10);
function sayName(name){
    console.log('hello ', name);
}
var sayAge = function(age){
    console.log(age);
};

函數(shù)聲明前置后,而函數(shù)表達式則不會前置,所以結(jié)果即:

sayAge(10);  // sayAge is not a function(報錯)。 (因為函數(shù)表達式不會被前置)
function sayName(name){
    console.log('hello ', name);
}
var sayAge = function(age){
    console.log(age);
};
sayName('world');  // hello world

11. 如下代碼的輸出是什么?寫出作用域鏈查找過程偽代碼

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

聲明提前,如下:

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

作用域鏈代碼,如下:

1. 
   globalContext = {
         AO {
               x : 10
               foo : function
               bar : function
         },
        Scope: null
   }
   foo.[[scope]] = globalContext.AO
   bar.[[scope]] = globalContext.AO
  全局變量 x = 10
2. 調(diào)用函數(shù)bar() 
   barContext = {
        AO:{
              x : 30
              foo : function
        },
        Scope:bar.[[scope]] = globalContext.AO
    }
    函數(shù)內(nèi)局部變量 x = 30

3. 調(diào)用函數(shù)foo() 
   fooContext = {
        AO:{ };
   },
   Scope:foo.[[scope]] = globalContext.AO
   //因為作用域鏈的關(guān)系, 全局變量 x = 10 , 所以 console.log(x) 的結(jié)果也是 10 。

12. 如下代碼的輸出是什么?寫出作用域鏈查找過程偽代碼

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

聲明提前,如下:

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

作用域鏈代碼,如下:

1.
   globalContext = {
        AO:{
              x : 10;
              bar : function
        },
        Scope: null
   }
   bar.[[scope]] = globalContext.AO
2. 調(diào)用函數(shù)bar()
   barContext = {
        AO:{
              x : 30;
             foo: function
        },
       Scope:bar.[[scope]] = globalContext.AO
   }
   foo.[[scope]] = barContext.AO
3. 調(diào)用函數(shù)foo()
   fooContext = {
        AO:{},
   }
   foo.[[scope]] = barContext.AO
   //調(diào)用foo時會在barContext.AO中找到x = 30。

13. 如下代碼的輸出是什么?寫出作用域鏈查找過程偽代碼

var x = 10;
bar() ;
function bar(){
     var x = 30;
     (function (){
           console.log(x)
     })();
}

聲明提前,如下:

var x ;
function bar(){
     var x = 30 ;
     (function (){
           console.log(x)
     })();
}
x = 10 ;
bar() ;

作用域鏈代碼,如下:

1.
    globalContext = {
        AO:{
            x:10
            bar:function
        },
        Scope:null
    }
    bar.[[scope]] = globalContext.AO
2.調(diào)用bar()
    barContext = {
        AO:{
            x:30
            function
        },
        Scope:bar.[[scope]] = globalContext.AO
    }
    function.[[scope]] = barContext.AO
3.調(diào)用立即執(zhí)行函數(shù)
    functionContext = {
        AO:{},
        Scope:function.[[scope]] = barContext.AO
    }
調(diào)用bar函數(shù)時,由于它的活動對象中 x=30,所以當自執(zhí)行函數(shù)執(zhí)行時,會先在它自己的AO中查找,找不到再向bar()函數(shù)的AO中查找,所以結(jié)果為30。

14. 如下代碼的輸出是什么?寫出作用域鏈查找過程偽代碼

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();
console.log(a);

作用域鏈代碼,如下:

1.
    globalContext = {
        AO:{
            a:1
            fn:function
            fn3:function
        },
        Scope:null
    }
    fn.[[scope]] = globalContext.AO
    fn3.[[scope]] = globalContext.AO

2.調(diào)用fn()
    fnContext = {
        AO:{
            a:undefined
            fn2:function
        },
        Scope:fn.[[scope]]  = globalContext.AO
    }
    fn2.[[scope]] = fnContext.AO

3.
    fn3Context = {
        AO:{
            a:200
        },
        Scope:fn3Context.[[scope]] = globalContext.AO
    }
    fn2ConText = {
        AO:{
            a:20
        },
        Scope:fn2ConText.[[scope]] = fnContext.AO
    }
    開始執(zhí)行
    console.log(a);    //undefined 打印
    var a = 5;            //fnContext中a變成5
    console.log(a);    //5
    a++ ;                  //fnContext.AO中a變?yōu)?

    調(diào)用fn3()
    fn3()中
    console.log(a);    //globalContext.AO中的a值為1,打印
    a = 200;              //globalContext.AO中的a變?yōu)?00

    調(diào)用fn2()
    console.log(a);   //fnContext.AO中的a值為6,打印
    a = 20;                //fnContext.AO中的a變?yōu)?0

    繼續(xù)執(zhí)行fn()
    console.log(a);    //fnContext.AO中的a值為20,打印

    fn()結(jié)束
    console.log(a);     //globalContext.AO中a值為200,打印

輸出的結(jié)果: undefined  5  1  6  20  200
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 1.函數(shù)聲明和函數(shù)表達式有什么區(qū)別 (*) 區(qū)別: 函數(shù)聲明后面的分號可加可不加,不加也不影響接下來語句的執(zhí)行,但...
    Sheldon_Yee閱讀 482評論 0 1
  • 1. 函數(shù)聲明和函數(shù)表達式有什么區(qū)別 使用function關(guān)鍵字聲明一個函數(shù)時,聲明不必放到調(diào)用的前面。//函數(shù)聲...
    _李祺閱讀 336評論 0 0
  • 函數(shù)聲明和函數(shù)表達式有什么區(qū)別? 函數(shù)聲明和函數(shù)表達式是EMACScript規(guī)定的兩種不同的聲明函數(shù)的方法。1.函...
    LeeoZz閱讀 455評論 0 1
  • 聲明前置和作用域也是JS 部分面試??键c 1.函數(shù)聲明和函數(shù)表達式有什么區(qū)別 函數(shù)聲明:使用function關(guān)鍵字...
    湖衣閱讀 283評論 0 0
  • 創(chuàng)造意義 想舉我的例子:我從三月中旬開始兼職,一周有四五天都在兼職,都是體力活,經(jīng)常站著腳疼,有時候真的很累,但是...
    一只正在成長的獅子閱讀 206評論 0 1

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