函數(shù)與作用域

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

  • 函數(shù)聲明:function命令聲明的代碼區(qū)塊蝠嘉,就是一個(gè)函數(shù)缘回。function命令后面是函數(shù)名秽誊,函數(shù)名后面是一對圓括號错览,里面是傳入函數(shù)的參數(shù)理澎。函數(shù)體放在大括號里面逞力。
function print(s) {
  console.log(s);
}

上面的代碼命名了一個(gè)print函數(shù),以后使用print()這種形式糠爬,就可以調(diào)用相應(yīng)的代碼寇荧。這叫做函數(shù)的聲明(Function Declaration)。

  • 函數(shù)表達(dá)式:除了用function命令聲明函數(shù)执隧,還可以采用變量賦值的寫法揩抡。
var print = function(s) {
  console.log(s);
};

這種寫法將一個(gè)匿名函數(shù)賦值給變量户侥。這時(shí),這個(gè)匿名函數(shù)又稱函數(shù)表達(dá)式(Function Expression)捅膘,因?yàn)橘x值語句的等號右側(cè)只能放表達(dá)式添祸。

采用函數(shù)表達(dá)式聲明函數(shù)時(shí),function命令后面不帶有函數(shù)名寻仗。如果加上函數(shù)名刃泌,該函數(shù)名只在函數(shù)體內(nèi)部有效,在函數(shù)體外部無效署尤。

var print = function x(){
  console.log(typeof x);
};

x
// ReferenceError: x is not defined

print()
// function

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

  • 變量聲明前置:JavaScript引擎的工作方式是,先解析代碼曹体,獲取所有被聲明的變量俗扇,然后再一行一行地運(yùn)行。這造成的結(jié)果箕别,就是所有的變量的聲明語句铜幽,都會被提升到代碼的頭部,這就叫做變量提升(hoisting)串稀。
console.log(a);
var a = 1;

上面代碼首先使用console.log方法除抛,在控制臺(console)顯示變量a的值。這時(shí)變量a還沒有聲明和賦值母截,所以這是一種錯(cuò)誤的做法到忽,但是實(shí)際上不會報(bào)錯(cuò)。因?yàn)榇嬖谧兞刻嵘蹇埽嬲\(yùn)行的是下面的代碼喘漏。

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

最后的結(jié)果是顯示undefined,表示變量a已聲明华烟,但還未賦值翩迈。
請注意,變量提升只對var命令聲明的變量有效盔夜,如果一個(gè)變量不是用var命令聲明的帽馋,就不會發(fā)生變量提升。

  • 函數(shù)聲明前置:JavaScript引擎將函數(shù)名視同變量名比吭,所以采用function命令聲明函數(shù)時(shí)绽族,整個(gè)函數(shù)會像變量聲明一樣,被提升到代碼頭部衩藤。所以吧慢,下面的代碼不會報(bào)錯(cuò)。
f();

function f() {}

表面上赏表,上面代碼好像在聲明之前就調(diào)用了函數(shù)f检诗。但是實(shí)際上匈仗,由于“變量提升”,函數(shù)f被提升到了代碼頭部逢慌,也就是在調(diào)用之前已經(jīng)聲明了悠轩。但是,如果采用賦值語句定義函數(shù)攻泼,JavaScript就會報(bào)錯(cuò)火架。

f();
var f = function (){};
// TypeError: undefined is not a function

上面的代碼等同于下面的形式。

var f;
f();
f = function () {};

上面代碼第二行忙菠,調(diào)用f的時(shí)候何鸡,f只是被聲明了,還沒有被賦值牛欢,等于undefined骡男,所以會報(bào)錯(cuò)。因此傍睹,如果同時(shí)采用function命令和賦值語句聲明同一個(gè)函數(shù)隔盛,最后總是采用賦值語句的定義。

3.arguments 是什么

由于JavaScript允許函數(shù)有不定數(shù)目的參數(shù)拾稳,所以我們需要一種機(jī)制吮炕,可以在函數(shù)體內(nèi)部讀取所有參數(shù)。這就是arguments對象的由來熊赖。

arguments對象包含了函數(shù)運(yùn)行時(shí)的所有參數(shù),arguments[0]就是第一個(gè)參數(shù)虑椎,arguments[1]就是第二個(gè)參數(shù)震鹉,以此類推。這個(gè)對象只有在函數(shù)體內(nèi)部捆姜,才可以使用传趾。

var f = function(one) {
  console.log(arguments[0]);
  console.log(arguments[1]);
  console.log(arguments[2]);
}

f(1, 2, 3)
// 1
// 2
// 3

需要注意的是,雖然arguments很像數(shù)組泥技,但它是一個(gè)對象浆兰。數(shù)組專有的方法(比如slice和forEach),不能在arguments對象上直接使用珊豹。

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

函數(shù)重載是指具有不同參數(shù)列表的同名函數(shù)簸呈。
JS中如果同一個(gè)函數(shù)被多次聲明,后面的聲明就會覆蓋前面的聲明店茶。
要實(shí)現(xiàn)重載蜕便,可以使用arguments參數(shù)

function overLoading() {
  // 根據(jù)arguments.length,對不同的值進(jìn)行不同的操作
  switch(arguments.length) {
    case 0:
      /*操作1的代碼寫在這里*/
      break;
    case 1:
      /*操作2的代碼寫在這里*/
      break;
    case 2:
      /*操作3的代碼寫在這里*/
        break;

     }
 
}

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

在Javascript中轿腺,一對圓括號()是一種運(yùn)算符两嘴,跟在函數(shù)名之后,表示調(diào)用該函數(shù)族壳。比如憔辫,print()就表示調(diào)用print函數(shù)。

有時(shí)仿荆,我們需要在定義函數(shù)之后贰您,立即調(diào)用該函數(shù)。這時(shí)赖歌,你不能在函數(shù)的定義之后加上圓括號枉圃,這會產(chǎn)生語法錯(cuò)誤。

function(){ /* code */ }();
// SyntaxError: Unexpected token (

產(chǎn)生這個(gè)錯(cuò)誤的原因是庐冯,function這個(gè)關(guān)鍵字即可以當(dāng)作語句孽亲,也可以當(dāng)作表達(dá)式。

// 語句
function f() {}

// 表達(dá)式
var f = function f() {}

為了避免解析上的歧義展父,JavaScript引擎規(guī)定返劲,如果function關(guān)鍵字出現(xiàn)在行首,一律解釋成語句栖茉。因此篮绿,JavaScript引擎看到行首是function關(guān)鍵字之后,認(rèn)為這一段都是函數(shù)的定義吕漂,不應(yīng)該以圓括號結(jié)尾亲配,所以就報(bào)錯(cuò)了。

解決方法就是不要讓function出現(xiàn)在行首惶凝,讓引擎將其理解成一個(gè)表達(dá)式吼虎。最簡單的處理,就是將其放在一個(gè)圓括號里面苍鲜。

(function () { /* code */ }());
// 或者
(function(){ /* code */ })();

上面兩種寫法都是以圓括號開頭思灰,引擎就會認(rèn)為后面跟的是一個(gè)表示式,而不是函數(shù)定義語句混滔,所以就避免了錯(cuò)誤洒疚。這就叫做“立即執(zhí)行函數(shù)表達(dá)式”(Immediately-Invoked Function Expression),簡稱IIFE坯屿。
注意油湖,上面兩種寫法最后的分號都是必須的。如果省略分號领跛,遇到連著兩個(gè)IIFE肺魁,可能就會報(bào)錯(cuò)。
推而廣之隔节,任何讓解釋器以表達(dá)式來處理函數(shù)定義的方法鹅经,都能產(chǎn)生同樣的效果寂呛,比如下面三種寫法。

var i = function(){ return 10; }();
true && function(){ /* code */ }();
0, function(){ /* code */ }();

甚至像下面這樣寫瘾晃,也是可以的贷痪。

!function(){ /* code */ }();
~function(){ /* code */ }();
-function(){ /* code */ }();
+function(){ /* code */ }();

通常情況下,只對匿名函數(shù)使用這種“立即執(zhí)行的函數(shù)表達(dá)式”蹦误。它的目的有兩個(gè):一是不必為函數(shù)命名劫拢,避免了污染全局變量;二是IIFE內(nèi)部形成了一個(gè)單獨(dú)的作用域强胰,可以封裝一些外部無法讀取的私有變量舱沧。

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

function factorial(n){
    if(n===1||n===0){
        return 1;
    }else if(n<0){
        return "負(fù)數(shù)無階乘";
    }
    return n*factorial(n-1);
}

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('饑人谷', 2, '男');
//'name:饑人谷' 'age:2' 'sex:男'
getInfo('小谷', 3);
//'name:饑人谷' 'age:2' 'sex:undefined'
getInfo('男');
//'name:男' 'age:undefined' 'sex:undefined'

8. 寫一個(gè)函數(shù)熟吏,返回參數(shù)的平方和?

function sumOfSquares() {
    var a = 0;
    for (i in arguments) {
        a = a + arguments[i]*arguments[i];
    }
    return a
}
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);
// undefined 在執(zhí)行console.log(a)時(shí)牵寺,由于變量提升,a只聲明恩脂,未定義
//Uncaught ReferenceError: b is not defined 代碼中未聲明變量b

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

    sayName('world');
    sayAge(10);
    function sayName(name){
        console.log('hello ', name);
    }
    var sayAge = function(age){
        console.log(age);
    };
//hello  world   由于函數(shù)名提升,函數(shù)定義可以放在執(zhí)行代碼后
//Uncaught TypeError: sayAge is not a function
//var sayAge = function(age) 只進(jìn)行sayAge變量名提升俩块,sayAge(10)前函數(shù)未定義

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

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

1.
globalContext = {
  AO: {
    x: 10
    foo: function
    bar: function
  },
  Scope: null
}
bar.[[scope]] = globalContext.AO
foo.[[scope]] = globalContext.AO
2.
barContext = {
  AO:{
      x:10
  }
  scope:globalContext.AO
}
3.
fooContext = {
  AO:{
       }
  scope:globalContext.AO
  }
}  
在 globalContext.AO找到x:10
輸出:10

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

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

1.
globalContext = {
  AO: {
    x: 10
    bar: function
  },
  Scope: null
}
bar.[[scope]] = globalContext.AO
2.
batContext = {
  AO: {
    x: 30
    foo: function
  },
  Scope:globalContext.AO
}
foo.[[scope]] = barContext.AO

3.
fooContext = {
  AO: {
  },
  Scope:barContext.AO
}
從barContext.AO中得x:30黎休,輸出為30

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

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

1.
globalContext = {
  AO: {
    x: 10
    bar: function
  },
  Scope: null
}
bar.[[scope]] = globalContext.AO
2.
barContext = {
  AO: {
    x: 30
  },
  scope:globalContext.AO
}
//(function (){
//    console.log(x)
//  })() 為立即執(zhí)行函數(shù)表達(dá)式,會屏蔽全局變量的污染玉凯,調(diào)用當(dāng)前bar的AO势腮,此時(shí)x=30,因此輸出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)
//undefined
//5
//1
//6
//20
//200

1.
globalContext = {
  AO: {
    a: 1
    fn: function
    fn3: function
  },
  Scope: null
}
fn[[scope]] = globalContext.AO
fn3[[scope]] = globalContext.AO
2.
fnContext = {
  AO: {
    a: undefined,然后為5嫉鲸,a++變6撑蒜,fn2后變20
    fn2: function
  },
  scope: globalContext.AO
}
fn2.[[scope]] = fnContext.AO
3.
fn2Context = {
  AO: {
  },
  scope:fnContext.AO
}
4.
fn3Context = {
  AO: {
  },
  scope: globalContext.AO
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末歹啼,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子座菠,更是在濱河造成了極大的恐慌狸眼,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浴滴,死亡現(xiàn)場離奇詭異拓萌,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)升略,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進(jìn)店門微王,熙熙樓的掌柜王于貴愁眉苦臉地迎上來屡限,“玉大人,你說我怎么就攤上這事炕倘【螅” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵罩旋,是天一觀的道長啊央。 經(jīng)常有香客問我,道長涨醋,這世上最難降的妖魔是什么瓜饥? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮浴骂,結(jié)果婚禮上乓土,老公的妹妹穿的比我還像新娘。我一直安慰自己靠闭,他們只是感情好帐我,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著愧膀,像睡著了一般拦键。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上檩淋,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天芬为,我揣著相機(jī)與錄音,去河邊找鬼蟀悦。 笑死媚朦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的日戈。 我是一名探鬼主播询张,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼浙炼!你這毒婦竟也來了份氧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤弯屈,失蹤者是張志新(化名)和其女友劉穎蜗帜,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體资厉,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡厅缺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片湘捎。...
    茶點(diǎn)故事閱讀 39,965評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡诀豁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出窥妇,到底是詐尸還是另有隱情且叁,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布秩伞,位于F島的核電站逞带,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏纱新。R本人自食惡果不足惜展氓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望脸爱。 院中可真熱鬧遇汞,春花似錦、人聲如沸簿废。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽族檬。三九已至歪赢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間单料,已是汗流浹背埋凯。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓击胜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親甩恼。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 1.函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別 函數(shù)就是一段可以反復(fù)調(diào)用的代碼塊沉颂。函數(shù)還能接受輸入的參數(shù)条摸,不同的參數(shù)會返回不同...
    徐國軍_plus閱讀 473評論 0 0
  • 一、函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別 區(qū)別:用函數(shù)聲明創(chuàng)建的函數(shù)可以在定義之前就進(jìn)行調(diào)用兆览;而用函數(shù)表達(dá)式創(chuàng)建的函數(shù)不...
    任少鵬閱讀 190評論 1 2
  • 1.函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別 function命令聲明的代碼區(qū)塊屈溉,就是一個(gè)函數(shù)塞关。function命令后面是函...
    饑人谷_Leon閱讀 283評論 0 0
  • 1抬探,函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別 1、背景介紹 定義函數(shù)的方法主要有三種: 1:函數(shù)聲明(Function De...
  • 也許回家,是一場夢吧小压。 因?yàn)闀r(shí)間過得真的太快了线梗。 周三晚上,父親微信視頻與我聊天怠益,說爺爺奶奶被車撞了仪搔,我有點(diǎn)慌張,...
    浮生夢蘇閱讀 306評論 0 1