函數(shù)與作用域

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

  • 區(qū)別:
    用函數(shù)聲明創(chuàng)建的函數(shù)可以在定義之前就進(jìn)行調(diào)用;而用函數(shù)表達(dá)式創(chuàng)建的函數(shù)不能在被賦值之前進(jìn)行調(diào)用。
  • 原因
    函數(shù)聲明語句“被提前”到外部腳本或外部函數(shù)作用域的頂部,所以以這種方式聲明的函數(shù),可以被在它定義之前出現(xiàn)的代碼所調(diào)用。
    而要使用一個以表達(dá)式方式定義的函數(shù)之前,必須把它賦值給一個變量。變量的聲明提前了,但給變量賦值是不會提前的,所以,以表達(dá)式方式定義的函數(shù)在定義之前無法調(diào)用。

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

  • 變量聲明前置:
    javascript的變量聲明具有hoisting機(jī)制,JavaScript引擎在執(zhí)行的時(shí)候,會把所有變量的聲明都提升到當(dāng)前作用域的最前面。

var a = 1;
function main() {
console.log(a);
var a = 2;
}
main()//輸出undefined
//解析如下

function main() {
    var a;
    console.log(a);
    a = 2;
}
main()
//所以輸出undefined```

* 函數(shù)的聲明前置
JavaScript的函數(shù)作用域是指在函數(shù)內(nèi)聲明的所有變量在函數(shù)體內(nèi)是始終可見的。這意味著變量可以先使用,后聲明。JavaScript的這一特性被非正式地稱為聲明提前(hoiosting),即JavaScript函數(shù)中所有變量的聲明都被“提前”至函數(shù)體的頂部。
>```
var scope = "global";
function f() {
   console.log(scope);//輸出underfined,而不是"global"
   var scope = "local"; //變量在這里賦初始值,但變量在函數(shù)體內(nèi)任何地方均是有定義的。
   console.log(scope);//輸出"local"
}
//上述過程等價(jià)于:將函數(shù)內(nèi)的變量聲明提前至函數(shù)頂部,但變量初始化(賦值)還在原來的位置。
var scope = "global";
function f() {
   var scope;//在函數(shù)頂部聲明了局部變量
   console.log(scope);//變量存在,但值是underfined
   scope = "local"; //初始化賦值
   console.log(scope);//
}```

三、arguments 是什么

標(biāo)識符arguments是指向?qū)崊ο蟮囊?,?shí)參對象是一個類數(shù)組對象(不能修改它,也不能用push來添加新元素等。但是可以訪問其中的元素,并且同時(shí)具有.length屬性)。
在函數(shù)代碼中,使用特殊對象 arguments,無需明確指出參數(shù)名,就能訪問它們。
例如,在函數(shù) sayHi() 中,第一個參數(shù)是 message。用 arguments[0] 也可以訪問這個值,即第一個參數(shù)的值(第一個參數(shù)位于位置 0,第二個參數(shù)位于位置 1,依此類推)。

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

所謂重載,是同一函數(shù)名,但是參數(shù)類型或參數(shù)個數(shù)不同的函數(shù)。
Javascript不像其他編程語言一樣具有函數(shù)簽名(函數(shù)簽名,簡單的說就是函數(shù)的接收參數(shù)類型和參數(shù)個數(shù))。所以Javascript是不能像其他語言一樣實(shí)現(xiàn)方法名相同,參數(shù)個數(shù)不同的這類重載的。
利用arguments,可以實(shí)現(xiàn)JavaScript的重載。
>         function showMessage(){
            if(arguments.length==1){
                console.log(arguments[0]);
            }else if( arguments.length==2){
                console.log(arguments[0]+"說:"+arguments[1]);
            }else{
                return false;
            }
        }
       showMessage("Hi!");
       showMessage("張三","Hi 你妹");

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

立即執(zhí)行函數(shù)就是當(dāng)我們在定義了函數(shù)之后需要立即執(zhí)行的函數(shù)。

( function(){…} ) ()
( function (){…} () )```
是兩種javascript立即執(zhí)行函數(shù)的常見寫法。

  • 為什么這么寫:

首先,要在函數(shù)體后面加括號就能立即調(diào)用,這個函數(shù)必須是函數(shù)表達(dá)式,不能是函數(shù)聲明。
下面代碼:

function(){ /* code */ }(); // SyntaxError: Unexpected token```
報(bào)錯了,這是為何?這是因?yàn)樵趈avascript代碼解釋時(shí),當(dāng)遇到function關(guān)鍵字時(shí),會默認(rèn)把它當(dāng)做是一個函數(shù)聲明,而不是函數(shù)表達(dá)式,如果沒有把它顯視地表達(dá)成函數(shù)表達(dá)式,就報(bào)錯了,因?yàn)楹瘮?shù)聲明需要一個函數(shù)名,而上面的代碼中函數(shù)沒有函數(shù)名。(以上代碼,也正是在執(zhí)行到第一個左括號(時(shí)報(bào)錯,因?yàn)?前理論上是應(yīng)該有個函數(shù)名的。)
如果我們給它函數(shù)名,然后加上()立即調(diào)用,同樣也會報(bào)錯:

function foo(){ /* code */ }(); // SyntaxError: Unexpected token```
為什么會這樣?在一個表達(dá)式后面加上括號,表示該表達(dá)式立即執(zhí)行;而如果是在一個語句后面加上括號,該括號完全和之前的語句不搭嘎,而只是一個分組操作符,用來控制運(yùn)算中的優(yōu)先級(小括號里的先運(yùn)算)。所以以上代碼等價(jià)于:

function foo(){ /* code */ }
(); // SyntaxError: Unexpected token )```
相當(dāng)于先聲明了一個叫foo的函數(shù),之后進(jìn)行()內(nèi)的表達(dá)式運(yùn)算,但是()(分組操作符)內(nèi)的表達(dá)式不能為空,所以報(bào)錯。

( function(){…} ) ()
( function (){…} () )```
為什么這樣就能立即執(zhí)行并且不報(bào)錯呢?因?yàn)樵趈avascript里,括號內(nèi)部不能包含語句,當(dāng)解析器對代碼進(jìn)行解釋的時(shí)候,先碰到了(),然后碰到function關(guān)鍵字就會自動將()里面的代碼識別為函數(shù)表達(dá)式而不是函數(shù)聲明。

  • 作用:

1. 創(chuàng)建只使用一次的函數(shù),并立即執(zhí)行它。
 2. 創(chuàng)建閉包,保存狀態(tài),隔離作用域。
 3. 作為獨(dú)立模塊存在(例子如jQuery),防止命名沖突,命名空間注入(模塊解耦)。

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

function recursion(n) {
    if (n===1) {
        return 1;
    }else {
        return n * recursion(n-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, '男');
/*輸出
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
*/

八、寫一個函數(shù),返回參數(shù)的平方和?

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

九、如下代碼的輸出?為什么

console.log(a);
    var a = 1;
    console.log(b);
以上可重寫為:
var a;//聲明前置
console.log(a);//輸出underfined
a = 1;
console.log(b);//ReferenceError: b is not defined

十、如下代碼的輸出?為什么

sayName('world');//輸出hello world,函數(shù)聲明前置
    sayAge(10);//ReferenceError: sayAge is not defined
    function sayName(name){
        console.log('hello ', name);
    }
    var sayAge = function(age){
        console.log(age);
    };

十一、如下代碼輸出什么? 寫出作用域鏈查找過程偽代碼

var x = 10
bar() 
function foo() {
  console.log(x)
}
function bar(){
  var x = 30
  foo()
}
//輸出10
/*
 1. globalContext = {
        AO: {
            x: 10
            foo: function
            bar: function
        }
        Scope: null
    }

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

 //調(diào)用 bar()
 2. barContext = {
        AO: {
            x: 30
        }
        Scope: globalContext.AO
    }
  
 //調(diào)用 foo() 
 3. fooContext = {
        AO: {}
        Scope: globalContext.AO
    }
*/

十二、如下代碼輸出什么? 寫出作用域鏈查找過程偽代碼

var x = 10;
bar() 
function bar(){
  var x = 30;
  function foo(){
    console.log(x) 
  }
  foo();
}   
//輸出30
/*
1. globalContext = {
        AO: {
            x: 10
            bar: function
        }
        Scope: null
    }

    bar.[[Scope]] = globalContext.AO

 //調(diào)用 bar() 
 2. barContext = {
        AO: {
            x: 30
            foo: function
        },
        Scope: globalContext.AO
    }

    foo.[[Scope]] = barContext.AO
  
 //調(diào)用 foo() 
 3. fooContext = {
        AO: {}
        Scope: barContext.AO
    }
*/

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

var x = 10;
bar() 
function bar(){
  var x = 30;
  (function (){
    console.log(x)
  })()
}
//輸出30
 /*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
    }
*/

十四、以下代碼輸出什么? 寫出作用域鏈查找過程偽代碼

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)
重寫為:
var a;
function fn(){
  var a;           //3.聲明fn()的局部變量a
  var a;
  function fn2(){  //4.聲明fn2()
    console.log(a);//13.fn2()未定義變量a,尋找父級,輸出6
    a=20;          //14.把fn()的a賦值為20
  }
  console.log(a);  //5.未賦值輸出underfined
  a=5;             //6.為fn()的局部變量a賦值為5
  console.log(a);  //7.輸出5
a++;               //8.a=6
fn3()              //9.調(diào)用fn3()
fn2()              //12.調(diào)用fn2()
console.log(a)     //15.輸出20
}
function fn3(){
  console.log(a);  //10.fn3()的作用域未定義變量a,尋找父級,輸出1
  a=200            //11.將全局a賦值為200
}
a=1;              //1.全局變量a賦值為1
fn()              //2.調(diào)用fn()
console.log(a)    //16.輸出200
//輸出
undefined
5
1
6
20
200
/*
 1. globalContext = {
        AO: {
            a: 1 -> 200
            fn: function
            fn3: function
        },
        Scope: null
    }

    fn.[[Scope]] = globalContext.AO
    fn3.[[Scope]] = globalContext.AO

 //調(diào)用 fn() 
 2. fnContext = {
        AO: {
            a: undefined -> 5 -> 6 -> 20
            fn2: function
        }
        Scope: globalContext.AO
    }

    fn2.[[Scope]] = fnContext.AO
  
 //調(diào)用 fn3()
 3. fn3Context = {
        AO: {}
        Scope: globalContext.AO
    }
 //調(diào)用 fn2() 
 3. fn2Context = {
        AO: {}
        Scope: fnContext.AO
    }
*/

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

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

  • 函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別? 函數(shù)聲明和函數(shù)表達(dá)式是EMACScript規(guī)定的兩種不同的聲明函數(shù)的方法。1.函...
    LeeoZz閱讀 442評論 0 1
  • 1,函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別 1、背景介紹 定義函數(shù)的方法主要有三種: 1:函數(shù)聲明(Function De...
  • 1.函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別 函數(shù)聲明不必放在調(diào)用前面 函數(shù)表達(dá)式必須放在調(diào)用前面 2.什么是變量的聲明前置...
    LINPENGISTHEONE閱讀 333評論 0 0
  • JavaScript中的函數(shù)運(yùn)行在它們被定義的作用域里,而不是它們被執(zhí)行的作用域里。 函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)...
    畢子歌閱讀 467評論 0 0
  • 1. 函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別 使用function關(guān)鍵字聲明一個函數(shù)時(shí),聲明不必放到調(diào)用的前面。//函數(shù)聲...
    _李祺閱讀 331評論 0 0

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