任務17-函數(shù)

問答:

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

在日常的任務中,JavaScript主要使用下面兩種方式創(chuàng)建函數(shù):

  • 函數(shù)聲明:
function sayName(){
   console.log('hunger');
}
sayName();
  • 函數(shù)表達式
var sayName = function(){
   console.log('hunger');
};

區(qū)別:
a. 函數(shù)聲明必須以function開頭直晨,否則谤草,則為表達式。
b. 函數(shù)表達式在語句的結尾要加上分號抵窒,表示語句結束,而函數(shù)聲明則不需要在結尾加上分號叠骑。
c. 函數(shù)聲明在JS解析的時候會進行函數(shù)提升李皇,即在同一個作用域內,不管函數(shù)聲明在哪里創(chuàng)建宙枷,它會提升至作用域頂部掉房。


函數(shù)聲明示例

但是函數(shù)表達式不會被提升,它的表現(xiàn)和聲明變量提升的規(guī)則相同慰丛,也就是說卓囚,函數(shù)名(這里指var后面定義的變量名)會聲明前置,但是函數(shù)表達式的賦值沒有被提前诅病,操作還是在原來的位置哪亿。

函數(shù)表達式示例

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

  • 變量的聲明前置:JavaScript引擎的工作方式是,先解析代碼蝇棉,獲取所有被聲明的變量讨阻,然后再一行一行地運行。這造成的結果篡殷,就是所有的變量的聲明語句变勇,都會被提升到代碼的頭部,這就叫做變量提升贴唇。
    如:

上面運行的代碼會報錯搀绣,是因為我們沒有聲明變量xxx。
接下來我們將聲明變量xxx


我們發(fā)現(xiàn)戳气,變量是在語句調用之后定義的链患,但是結果并沒有報錯。這就是變量聲明前置的作用瓶您。JS解析器會把當前作用域內聲明的所有變量和函數(shù)都放到作用域的開始麻捻。但是對于變量來說,它只是把變量的聲明提到了作用域的開始呀袱,而變量的賦值仍然按照順序執(zhí)行贸毕。那么沒有被賦值的變量會自動賦值為undefined,所以此處運行的結果為初始值undefined夜赵。它等同于如下代碼:

var xxx;
console.log(xxx);
xxx = 2;

再來看看如下的列子:


我們先聲明一個全局變量xxx明棍,然后再函數(shù)內部定義一個局部變量。我們希望是第一次打印出來的結果是全局范圍內定義的xxx變量寇僧,第二次打印出來的是局部變量xxx的值摊腋。但是輸出結果并沒有跟我們設想的一樣,原因就是定義的局部變量在其作用域內聲明提前了嘁傀。故第一次打印出來的是沒有賦值的undefined兴蒸,第二次打印出12。它等同于如下代碼:

   xxx = 2;
(function(){
         var xxx;
         console.log(xxx);
         xxx = 12;
         console.log(xxx);
})();
  • 函數(shù)的聲明前置:和變量的聲明會前置一樣细办,函數(shù)聲明同樣會前置橙凳,如果我們使用函數(shù)表達式那么規(guī)則和變量一樣;如果我們使用函數(shù)聲明的方式笑撞,那么即使函數(shù)寫在最后也可以在前面語句調用岛啸,前提是函數(shù)聲明部分已經被下載到本地。
  • 函數(shù)表達式:

    在上面代碼中娃殖,變量sayAge被前置了值戳,但是它的賦值并沒有被提前,這樣就印證了函數(shù)表達式的提前和變量提前是一回事了炉爆。上面的代碼等同于:

var sayAge;
sayAge(10);
sayAge = function(age){
      console.log(age);
}
  • 函數(shù)聲明:


    函數(shù)聲明并不僅僅是函數(shù)名被提前了,整個函數(shù)的定義都被提前了。它等同于如下代碼:

function fn(){
  console.log('1');
}
fn();

總結:

  • 變量聲明會提前到作用域的頂部芬首,而賦值會被保留在原地赴捞,仍然按照次序執(zhí)行。
  • 函數(shù)表達式是變量前置了郁稍,但是賦值沒有提前赦政。和變量提升一樣。
  • 函數(shù)聲明整個會被前置到變量聲明的后面耀怜,即使函數(shù)寫在最后面也可以在前面語句調用恢着。
    那么,我們在分析代碼的時候财破,可以把變量和函數(shù)聲明放在作用域的頂部掰派,這樣分析出來的結果一般不容易出錯。

3. arguments 是什么 (*)

arguments是JS里的一個內置對象,它只在函數(shù)內部有效左痢,可以在函數(shù)內部通過使用arguments對象來獲取函數(shù)的所有參數(shù)靡羡。這個對象為傳遞給函數(shù)的每個參數(shù)建立一個條目,條目的索引從0開始俊性。參數(shù)也可以重新被賦值略步。

arguments對象就像數(shù)組(array),但是它卻不是真正的數(shù)組定页,除了length趟薄,它沒有數(shù)組所特有的屬性和方法。
當一個函數(shù)的參數(shù)數(shù)量比它顯示聲明參數(shù)數(shù)量更多時候典徊,我們就可以使用arguments對象獲取到該函數(shù)的所有傳入參數(shù)竟趾。
如:

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

重載是很多面向對象語言實現(xiàn)多態(tài)的手段之一,在靜態(tài)語言中確定一個函數(shù)的手段是靠方法簽名——函數(shù)名+參數(shù)列表宫峦,也就是說相同名字的函數(shù)參數(shù)個數(shù)不同或者順序不同都被認為是不同的函數(shù)岔帽,稱為函數(shù)重載。
在JavaScript中沒有函數(shù)重載的概念导绷,函數(shù)通過名字確定唯一性犀勒,參數(shù)不同也被認為是相同的函數(shù),后面的覆蓋前面的妥曲。
在JavaScript中可以通過arguments來實現(xiàn)函數(shù)的重載贾费,如:

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

立即執(zhí)行函數(shù)表達式(Immediately-Invoked Function Expression)檐盟,是將函數(shù)定義放在一個圓括號里褂萧,讓JavaScript引擎將其理解為一個表達式,再在函數(shù)的定義后面加一個()葵萎,以達到定義函數(shù)后立即調用該函數(shù)的效果导犹。有下面兩種寫法:
<pre>
(function(){ /code/ }());
(function(){ /code/ })();</pre>

如:

作用:

  • 封裝大量的工作而不會在背后遺留任何全局變量唱凯。
  • 定義的所有變量都會成為立即執(zhí)行函數(shù)的局部變量,所以不用擔心這些臨時變量會污染全局空間谎痢。
  • 可以將獨立的功能封裝在自包含模塊中磕昼。
    注意:立即執(zhí)行函數(shù)通常作為一個單獨模塊使用。一般沒有問題节猿,但是票从,建議在自己寫的立即執(zhí)行函數(shù)前加分號,這樣可以有效地與前面代碼進行隔離滨嘱。否則峰鄙,可能出現(xiàn)意想不到的錯誤。

6. 什么是函數(shù)的作用域鏈 (****)

  • 作用域:作用域就是變量與函數(shù)的可訪問范圍太雨,即作用域控制著變量與函數(shù)的可見性和生命周期吟榴。在JavaScript中,變量的作用域有全局作用域和局部作用域兩種躺彬。
  • 全局作用域:在代碼中任何地方都能訪問到的對象擁有全局作用域煤墙,一般來說以下幾種情形擁有全局作用域:
    a. 最外層函數(shù)和在最外面定義的變量擁有全局作用域
var a = 2;
function fn(){
   var b;
   var c;
   function fn2(){
      console.log(a);
      console.log(b);
    }
    b = 3;
    fn2();
    c = 4;
} 
fn();
//在這段代碼中,變量a 和函數(shù)fn時擁有全局作用域宪拥,而在函數(shù)里面的變量和函數(shù)并不擁有全局作用域

b. 所有未定義直接賦值的變量自動聲明為擁有全局作用域

function fn(){
   var a = 2;
   b = 3;
   console.log(a);
}//變量b用于全局作用域仿野,而變量a則不是。

c. 所有window對象的屬性擁有全局作用域

  • 局部作用域:和全局作用域相反她君,局部作用域一般只在固定的代碼片段內可訪問到脚作,最常見的如函數(shù)內部,所以在一些地方也會看到有人把這種作用域稱為函數(shù)作用域缔刹。
function fn(){
   var a = 2;
   function fn2(){
      console.log(a);
    }
  fn2();
} //在此代碼中變量a和函數(shù)fn2用于局部作用域
  • 作用域鏈:當代碼在一個環(huán)境中執(zhí)行時球涛,會創(chuàng)建變量對象的一個作用域鏈(scope chain)。作用域鏈的用途校镐,是保證對執(zhí)行環(huán)境有權訪問的所有變量和函數(shù)的有序訪問亿扁。作用域鏈的前端,始終都是當前執(zhí)行的代碼所在環(huán)境的變量對象鸟廓。如果這個環(huán)境是函數(shù)从祝,則將其活動對象(activation object)作為變量對象∫眨活動對象在最開始時只包含一個變量牍陌,即arguments對象(這個對象在全局環(huán)境中是不存在的)。作用域鏈中的下一個變量對象來自包含(外部)環(huán)境员咽,而再下一個變量對象則來自下一個包含環(huán)境毒涧。這樣,一直延續(xù)到全局執(zhí)行環(huán)境贝室;全局執(zhí)行環(huán)境的變量對象始終都是作用域鏈中的最后一個對象契讲。
    參考:
    JavaScript 開發(fā)進階:理解 JavaScript 作用域和作用域鏈

代碼:

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, '男'); 
    getInfo('hunger', 28); 
    getInfo('男');

輸出結果:

//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. 寫一個函數(shù),返回參數(shù)的平方和怀泊?如 (難度**)

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

3. 如下代碼的輸出茫藏?為什么 (難度*)

console.log(a); //輸出undefined,因為變量提升了误趴,但是沒有賦值
 var a = 1; 
 console.log(b);//輸出b is not defined霹琼,因為變量b沒有被申明`

4. 如下代碼的輸出?為什么 (難度*)

sayName('world'); //輸出結果為hello world
sayAge(10); //輸出結果為Uncaught TypeError: sayAge is not a function
function sayName(name){ 
      console.log('hello ', name); 
} 
var sayAge = function(age){ 
      console.log(age); 
};

第一行代碼由于函數(shù)聲明提升凉当,整個sayName函數(shù)提到代碼頭部枣申,則在執(zhí)行sayName(“world”)時輸出正常結果;第二航代碼由于sayAge是函數(shù)表達式看杭,則JS引擎在解析代碼時忠藤,只是把var sayAge提到代碼的頭部,并未把整個函數(shù)部分提到代碼頭部楼雹,所以在執(zhí)行時會報錯模孩。

5. 如下代碼的輸出?為什么 (難度**)

function fn(){} 
var 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 fn(fn2){
    var fn2;//變量聲明提升
    function fn2(){
         console.log('fnnn2');
   }//函數(shù)聲明提升
   console.log(fn2);//當函數(shù)執(zhí)行命令有沖突時,函數(shù)載入的順序是變量>函數(shù)>參數(shù)桂肌,此時肯定是輸出函數(shù)数焊。
  fn2=3;
  console.log(fn2);//此時fn2被賦值3,因為在同一作用域內崎场,定義了同一個名字的變量和方法時佩耳,無論順序如何,變量的賦值會覆蓋方法的賦值谭跨。
  console.log(fn);//當函數(shù)執(zhí)行命令有沖突時干厚,函數(shù)載入的順序是變量>函數(shù)>參數(shù),此時肯定是輸出函數(shù)饺蚊。
}
fn(10); 

輸出結果為:

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

7. 如下代碼的輸出萍诱?為什么 (難度***)

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

上述代碼相當于:

var fn;
function fn(fn){
       console.log(fn);
}
fn = 1;
console.log(fn(fn));//輸出結果:Uncaught TypeError: fn is not a function(…)。在執(zhí)行時污呼,正確代碼應該是函數(shù)(參數(shù))裕坊,在這個習題中,由于變量命名提升燕酷,代碼形式為變量(變量)籍凝,那么交給瀏覽器去執(zhí)行時會輸出:fn is not a function(…)周瞎。

8. 如下代碼的輸出?為什么 (難度**)

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

上述代碼相當于:

var i;
 var j;
//變量提升饵蒂,將其 提到代碼頭部
 console.log(i);//undefined声诸,此時變量i還未被賦值
 console.log(j);//undefined,此時變量j還未被賦值
 for(var i = 0; i<10; i++){
       var j = 100;
 console.log(i);//10 在for循環(huán)執(zhí)行后退盯,i為10彼乌,for循環(huán)語句不會前置,其定義的變量自然就是全局變量渊迁,所以能夠被解析慰照,正常順序執(zhí)行并顯示。
 console.log(j);`//100 在for循環(huán)執(zhí)行后琉朽,j為100毒租。

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;
 //變量提升箱叁,將var i和var fn提升到代碼頭部
 function fn(){
     var i;
     function fn2(){
          i = 100;
     }
    console.log(i);//輸出undefined墅垮,因為變量i聲明了但是沒有賦值
    i = 99;
    fn2();//執(zhí)行后i為100,覆蓋了i = 99
    console.log(i);//輸出100
}
fn();
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,9,8,7,6,5,4,3,2
/*function前后加了圓括號算色,表示該函數(shù)為立即執(zhí)行函數(shù),因此會馬上執(zhí)行孤个,尾部賦值n=10,得到n-1=9剃允,然后n=9,繼續(xù)循環(huán),直到n=2時齐鲤,滿足if條件斥废,return終止,不執(zhí)行n-1给郊;所以最終結果為2*/
 console.log(say);`//輸出0
/*因為在該作用域中牡肉,變量say已經被賦值了0,在同一個作用域中淆九,變量和方法同名時统锤,無論順序如何,變量的賦值會覆蓋方法的賦值*/

本文版權歸本人及饑人谷所有炭庙,轉載請注明出處

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末饲窿,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子焕蹄,更是在濱河造成了極大的恐慌逾雄,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鸦泳,居然都是意外死亡银锻,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門做鹰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來击纬,“玉大人,你說我怎么就攤上這事钾麸「瘢” “怎么了?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵喂走,是天一觀的道長殃饿。 經常有香客問我谋作,道長芋肠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任遵蚜,我火速辦了婚禮帖池,結果婚禮上,老公的妹妹穿的比我還像新娘吭净。我一直安慰自己睡汹,他們只是感情好,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布寂殉。 她就那樣靜靜地躺著囚巴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪友扰。 梳的紋絲不亂的頭發(fā)上彤叉,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天,我揣著相機與錄音村怪,去河邊找鬼秽浇。 笑死,一個胖子當著我的面吹牛甚负,可吹牛的內容都是我干的柬焕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼梭域,長吁一口氣:“原來是場噩夢啊……” “哼斑举!你這毒婦竟也來了?” 一聲冷哼從身側響起病涨,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤富玷,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體凌彬,經...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡沸柔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了铲敛。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片褐澎。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖伐蒋,靈堂內的尸體忽然破棺而出工三,到底是詐尸還是另有隱情,我是刑警寧澤先鱼,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布俭正,位于F島的核電站,受9級特大地震影響焙畔,放射性物質發(fā)生泄漏掸读。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一宏多、第九天 我趴在偏房一處隱蔽的房頂上張望儿惫。 院中可真熱鬧,春花似錦伸但、人聲如沸肾请。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽铛铁。三九已至,卻和暖如春却妨,著一層夾襖步出監(jiān)牢的瞬間饵逐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工管呵, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留梳毙,地道東北人。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓捐下,卻偏偏與公主長得像账锹,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子坷襟,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內容

  • 一奸柬、函數(shù)聲明和函數(shù)表達式有什么區(qū)別?(*) ** 1.ECMAScript里面規(guī)定了三種聲明函數(shù)的方式**①構造函...
    鴻鵠飛天閱讀 458評論 0 0
  • 問答題 函數(shù)聲明和函數(shù)表達式有什么區(qū)別 (*)答://函數(shù)聲明function hello(){ conso...
    饑人谷_桶飯閱讀 245評論 0 0
  • 1.函數(shù)聲明和函數(shù)表達式有什么區(qū)別 (*) 函數(shù)聲明 函數(shù)表達式 函數(shù)聲明:函數(shù)調用可以發(fā)生在函數(shù)聲明之前,例如下...
    TimeLesser閱讀 395評論 4 4
  • 1.函數(shù)聲明和函數(shù)表達式的區(qū)別婴程? 在ECMAScript中廓奕,創(chuàng)建函數(shù)的最常用的兩個方法是函數(shù)表達式和函數(shù)聲明,兩者...
    GarenWang閱讀 533評論 0 0
  • [ 倫敦街頭攝影與徒步旅行手記(連載之5-1)] - Camden, a centre for the arts,...
    學院巷1號閱讀 1,043評論 2 12