JS函數(shù)與作用域

作用域

在JS中(非ES6),只有函數(shù)作用域,沒有塊作用域。
例如,for循環(huán),while等{}內(nèi)部的變量其實(shí)是和外部處于同一個(gè)作用域的:

  for (var i =1; i < 5; i ++) {
      var a = 3;
  }
  console.log(a); //3  此時(shí)沒有輸出undefined,說明a和for循環(huán)內(nèi)部的a是同一個(gè)作用域。

所以只有函數(shù)作用域

    function fn () {
        var a = 1;
        if (a > 2) {
            var b = 3;
        }
        console.log(b);
    }
    fn();
    console.log(a);

上述代碼的運(yùn)行結(jié)果等價(jià)于:

    function fn () {
        var a; // 進(jìn)行變量的提升
        var b; // 定義b是在if的{}內(nèi)進(jìn)行的,但是沒有塊作用域,實(shí)質(zhì)上作用域還是fn內(nèi)部,a和b是同一個(gè)作用域
        a = 1;
        if (a > 2) { //a沒有滿足條件,所以不會執(zhí)行給b賦值的語句,所以n僅僅聲明了但是沒有賦值,console.log(b)的結(jié)果是undefined
            b = 3;
        }
        console.log(b); //undefined
    }
    fn();
    console.log(a); // a is not defined  報(bào)錯(cuò),因?yàn)樵诋?dāng)前的作用域即全局作用域中,a并沒有定義,a只在fn的作用域里定義了。

var

  • var如果重復(fù)聲明一個(gè)已經(jīng)存在的變量時(shí),原來的變量的值是不會變的。
var a =1;
var a;
var a;
var a;
console.log(a); // 1      重復(fù)聲明不會改變。
  • 不加var的作用
    不寫var會聲明一個(gè)全局變量,所以不建議不寫var,即使需要全局變量,也要在全局作用域中使用var聲明變量。
    function fn () {
        a = 1; //沒有用var聲明,a其實(shí)是一個(gè)全局變量,在外部作用域中也能訪問
    }
    fn();
    console.log(a);//1 說明全局作用域中也能訪問a

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

有三種聲明函數(shù)的方式:

  • 構(gòu)造函數(shù):var doSomething = new Function("console.log('hello,deejay')"); 不推薦使用
  • 函數(shù)聲明:
  function doSomething () {    // 函數(shù)聲明
        console.log('hello,deejay');
  }
  doSomething();    // 調(diào)用也可以放到聲明的前面
  • 函數(shù)表達(dá)式:
  var doSomething = function () {  //函數(shù)表達(dá)式
    console.log("hello,deejay");  
  }
  doSomething(); // 表達(dá)式調(diào)用只能寫在賦值聲明后面

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

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

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

    sayHello();

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

上述代碼在解析時(shí)其實(shí)為

    var a;
    function sayHello() {console.log('hello,deejay');} //解析時(shí),var聲明的變量和function聲明的函數(shù) 會前置


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

另外,如果有變量名和函數(shù)聲明的函數(shù)名相同的情況,后面的值會覆蓋前面的值,產(chǎn)生報(bào)錯(cuò)。
對于函數(shù)表達(dá)式定義的函數(shù),前置的方式跟var一個(gè)變量沒什么區(qū)別。

    console.log(sayHello);//undefined  

    var sayHello = function () {
        console.log('hello,deejay');
    }

    sayHello(); // 特別要注意,對于函數(shù)表達(dá)式定義的函數(shù),只能先定義,然后再調(diào)用,不然會報(bào)錯(cuò)。

上述代碼等價(jià)于

    var sayHello;

    console.log(sayHello);//undefined

    sayHello = function () {
        console.log('hello,deejay');
    }

    sayHello(); // 特別要注意,對于函數(shù)表達(dá)式定義的函數(shù),只能先定義,然后再調(diào)用,不然會報(bào)錯(cuò)。
  • 函數(shù)內(nèi)部的聲明前置
    在一個(gè)作用域內(nèi),var定義的變量和function聲明的函數(shù)會前置,那么在函數(shù)內(nèi)部的作用域中,前置規(guī)則也是一樣的。
        function doSomething () {
        console.log(a); // undefined
        var a = 3;
        console.log(a); //3

        //上面代碼其實(shí)等價(jià)于下面代碼:
//        var a;
//        console.log(a); // undefined
//        a = 3;
//        console.log(a); //3
    }
    doSomething(); //調(diào)用函數(shù),進(jìn)入函數(shù)作用域
  • 變量和函數(shù)命名沖突時(shí)
    當(dāng)命名發(fā)生沖突時(shí),先進(jìn)行前置,再進(jìn)行覆蓋
    var fn = 3;
    function fn () {}
    console.log(fn); //3

上述代碼等價(jià)于

    var fn;
    function fn() {} //此時(shí)fn為函數(shù)
//    console.log(typeof fn); //function
    fn = 3;
//    console.log(typeof fn); //number
    console.log(fn); //3

同理:

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

等價(jià)于

    function fn() {}  // fn為一個(gè)全局函數(shù)
    var fn;   // 前面fn函數(shù)已經(jīng)存在,此時(shí)兵沒有給fn賦值,所以fn仍然是一個(gè)函數(shù)
 //   console.log(typeof fn);  //function
    fn = 3;  // 此時(shí)給fn賦值了之后,fn變?yōu)閿?shù)值
 //    console.log(typeof fn); //number
    console.log(fn); // 3
  • 函數(shù)名和參數(shù)名重名時(shí),即如下情況:
    function fn (fn) {
        console.log(fn);

        var fn = 3;
        console.log(fn);
    }
    fn(5); //5 3

此時(shí)運(yùn)行的過程等價(jià)于:

        function fn (fn) {
        var fn = 5;//這條語句是JS自動(dòng)隱藏添加的,當(dāng)傳入?yún)?shù)時(shí),給fn賦值

        var fn;  // 函數(shù)內(nèi)部作用域變量提升
        console.log(fn);  // 此時(shí)輸出的為傳入的已經(jīng)賦值的參數(shù)fn,而不是undefined

        fn = 3;
        console.log(fn); // 輸出的是當(dāng)前作用域內(nèi)的局部變量fn
    }

3.arguments 是什么

在函數(shù)內(nèi)部,可以使用arguments對象獲取到該函數(shù)的所有傳入?yún)?shù),是一個(gè)類數(shù)組對象。

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

    }
    getInfo('deejay',21,'male');

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

JS沒有重載! 同名的函數(shù)會覆蓋,但是可以在函數(shù)體內(nèi)針對不同的參數(shù)調(diào)用執(zhí)行相應(yīng)的邏輯

可以模擬重載,舉例說明:

      var getInfo = function (name,age,sex) {
        if (name) {
            console.log(name);
        }
        if (age) {
           console.log(age);
        }
        if (sex) {
            console.log(sex)
        }
    }

    getInfo('deejay',21); //deejay 21 
    getInfo('deejay',21,'male'); //deejay 21 male

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

    (function () {
        console.log('hello,deejay');
    })();

創(chuàng)建一個(gè)匿名函數(shù)并且立即調(diào)用它,一般用于隔離作用域(因?yàn)槠鋬?nèi)部的函數(shù)作用域不受外部作用域的影響)

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

遞歸
  1. 自己調(diào)用自己
  2. 設(shè)定終止條件
  3. 優(yōu)點(diǎn):算法簡單
  4. 缺點(diǎn):效率低

求n!的遞歸實(shí)現(xiàn):


    function fn (n) {
        if (n <= 0){
            console.log('n為正整數(shù)');
            return;
        }
        else if (n === 1) {
            return 1;
        }
        else if (n >=1 ){
            return n * fn(n - 1);
        }
    }
    var result = fn(5);
    console.log(result);

7. 分析輸出結(jié)果

  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('deejay', 21, '男');
        getInfo('dee', 3);
        getInfo('男');

輸出結(jié)果為:

    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('deejay', 21, '男');
//    輸出結(jié)果為:
//    name: deejay
//    age: 2
//    sex: 男
//    ['deejay',21,'男']
//    name valley

    getInfo('deejay', 3);
//    輸出結(jié)果為
//    name: deejay
//    age: 3
//    sex: undefined
//    ['deejay',3]
//    name valley

    getInfo('男');
//    輸出結(jié)果為
//    name: 男
//    age: undefined
//    sex: undefined
//    ['男']
//    name valley

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

     function sumOfSquares(){
    
     }
      var result2 = sumOfSquares(1,3)
     console.log(result)  //29
     console.log(result2)  //10

平方和代碼如下:

    function sumOfSquares(){
        var sum = 0;
        for (var i = 1; i <= arguments.length; i ++) {
            sum += Math.pow(arguments[i-1],2);
        }
        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);

輸出解釋如下:

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


//    等價(jià)于
    var a;
    console.log(a); //undefined  預(yù)解析,聲明了a,但是沒賦值,為undefined
    a = 1;
    console.log(b); //Uncaught ReferenceError: b is not defined  沒有聲明b,報(bào)錯(cuò)

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

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

解釋如下:

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

//    表達(dá)式定義的函數(shù),在進(jìn)行前置的時(shí)候,跟用var聲明的變量規(guī)則一樣
//    等價(jià)于:
//    function sayName(name){
//        console.log('hello ', name);
//    }
//    var sayAge;
//    sayName('world');//hello, world
//    sayAge(10); //Uncaught TypeError: sayAge is not a function 此時(shí)sayAge()只是被聲明,并不是一個(gè)函數(shù), 報(bào)錯(cuò)
//    sayAge = function (age) {
//        console.log(age);
//    }

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

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

偽代碼如下:

    GlobalContext = {
        AO: {
            x: 10,
            foo: function () {},
            bar: function () {},
        },
    }
    foo.[[scope]] = GlobalContext.AO;
    bar.[[scope]] = GlobalContext.AO;

    fooContext = {
        AO: {},
        scope: GlobalContext.AO
    }

    barContext = {
        AO:{
            x:30,
        },
        scope: GlobalContext.AO
    }

可以看出輸出的是GlobalContext.AO中的x的值,為10。

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

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

偽代碼如下:

    GlobalContext = {
        AO: {
            x:10,
            bar: function() {}
        },
    }
    bar.[[scope]] = GlobalContext.AO
    
    barContext = {
        AO : {
            x: 30,
            foo: function () {}
        },
        scope: GlobalContext.AO
    }
    
    fooContext = {
        AO: {},
        scope: barContext.AO
    }

很明顯輸出的是barContext.AO中的x值,為30

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

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

偽代碼如下:

    GlobalContext = {
        AO: {
            x: 10,
            bar: function() {}
        }
    }
    bar.[[scope]] = GlobalContext.AO
    
    barContext = {
        AO: {
            x: 30,
            匿名函數(shù): function () {}
        },
        scope: GlobalContext.AO
    }
    
    匿名函數(shù)Context = {
        AO: {},
        scope: barContext.AO
    }

顯然輸出的是barContext.AO中的x值,為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)

偽代碼為:

  開始執(zhí)行程序時(shí)的狀態(tài)值為:
    GlobalContext = {
        AO: {
            a:1,
            fn: function () {},
            fn3: function () {}
        },
    }
    fnContext = {
        AO: {
            a:undefined,//解析時(shí)的值是undefined,
            fn2: function () {}
        },
        scope: GlobalContext.AO // fn的上一級作用域?yàn)間lobal
    }
    fn3Context = {
        AO: {
            沒有任何活動(dòng)對象,注意:a = 200,沒有用var聲明,不是當(dāng)前作用域即fn3的作用域中的活動(dòng)對象
        },
        scope: GlobalContext.AO  // fn3的上一級為global
    }
    fn2Context = {
        AO: {
            沒有任何活動(dòng)對象,注意:a = 20,沒有用var聲明,不是當(dāng)前作用域即fn2的作用域中的活動(dòng)對象
        }, 
        scope: fnContext.AO // fn2的上一級作用域?yàn)閒n
    }

最終的輸出結(jié)果為:

    var a = 1;
    function fn (){
        console.log(a); //undefined
        var a = 5;
        console.log(a); //5
        a++;
        var a;
        fn3();
        fn2();
        console.log(a); //20

        function fn2() {
            console.log(a); //6
            a = 20; //改變了fn中的a(6 ----> 20)
        }
    }
    function fn3 () {
        console.log(a); //1
        a = 200; //改變了全局中的a (1 ----> 200)
    }
    fn();
    console.log(a);  //200

按照運(yùn)行順序依次輸出為: undefined 5 1 6 20 200

最后編輯于
?著作權(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ū)別 JavaScript 中需要?jiǎng)?chuàng)建函數(shù)的話,有兩種方法:函數(shù)聲明、函數(shù)表達(dá)式,各自寫...
    蕭雪圣閱讀 1,035評論 2 2
  • 1.函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別 函數(shù)聲明 代碼執(zhí)行時(shí)函數(shù)聲明會被提升到最前執(zhí)行,所以函數(shù)的調(diào)用與函數(shù)聲明的順序...
    Feiyu_有貓病閱讀 468評論 0 0
  • 函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別? 函數(shù)聲明和函數(shù)表達(dá)式是EMACScript規(guī)定的兩種不同的聲明函數(shù)的方法。1.函...
    LeeoZz閱讀 444評論 0 1
  • 如題,本文介紹函數(shù)與作用域的相關(guān)知識 1.函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別 函數(shù)聲明:使用function關(guān)鍵字可以...
    hahahahaqwert閱讀 321評論 0 0
  • JavaScript中的函數(shù)運(yùn)行在它們被定義的作用域里,而不是它們被執(zhí)行的作用域里。 函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)...
    畢子歌閱讀 467評論 0 0

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