前端入門09 -- JavaScript之函數(shù)论巍,作用域鏈烛谊,閉包

函數(shù)

  • 函數(shù)也是一個(gè)對象;
  • 函數(shù):把一個(gè)或者多功能通過函數(shù)的方式封裝起來嘉汰,對外只提供一個(gè)簡單的函數(shù)接口丹禀;
  • 聲明定義函數(shù)的三種方式:
    • 利用函數(shù)關(guān)鍵字function 自定義函數(shù);
    • 函數(shù)表達(dá)式鞋怀,用一個(gè)變量接收函數(shù)表達(dá)式双泪;
    • 箭頭函數(shù),用一個(gè)變量接收定義的箭頭函數(shù)密似;
    <script>
        ///1.function關(guān)鍵字
        ///addNum是一個(gè)函數(shù)
        function addNum(a,b) {
            return a + b
        }
        addNum(100,200)
        ///2.函數(shù)表達(dá)式 
        ///func是變量名 不是函數(shù)名 后面的表達(dá)式才是函數(shù) 屬于匿名函數(shù)
        var func = function(x) {
            console.log(x)
        }
        func('yanzi')

        ///3.箭頭函數(shù)
        var func2 = () => {
            console.log('我是一個(gè)箭頭函數(shù)');
        }
        func2();
    </script>
    <script>
        //定義聲明函數(shù)
        function getSum() {
            var sum = 0
            for (var i = 0; i < 100; i++) {
                sum += i
            }   
            console.log(sum);
        }
        //調(diào)用函數(shù)
        getSum()
    </script>

函數(shù)的參數(shù)

  • 行參:形式上的參數(shù)焙矛,函數(shù)定義的時(shí)候傳遞的參數(shù) 當(dāng)前并不知道是什么;
  • 實(shí)參:實(shí)際上的參數(shù)残腌,函數(shù)調(diào)用的時(shí)候傳遞的參數(shù) 實(shí)參是傳遞給行參的村斟;
  • 函數(shù)的參數(shù)可以有贫导,也可以沒有個(gè)數(shù)不限;
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script>
        //定義聲明函數(shù)
        function getSum(num1,num2) {
            console.log(num1 + num2)
        }
        //調(diào)用函數(shù)
        getSum(100,200)
    </script>
</head>
<body>
</body>
</html>
  • 函數(shù)形參實(shí)參的個(gè)數(shù)匹配問題:
    • 如果實(shí)參的個(gè)數(shù)和形參的個(gè)數(shù)一致蟆盹,則正常輸出結(jié)果孩灯;
    • 如果實(shí)參的個(gè)數(shù)多于形參的個(gè)數(shù),會取到形參的個(gè)數(shù)逾滥;
    • 如果實(shí)參的個(gè)數(shù)小于形參的個(gè)數(shù)钱反,多于的形參定義為undefined,最終的結(jié)果就是NaN匣距;
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script>
        //定義聲明函數(shù)
        function getSum(num1,num2) {
            console.log(num1 + num2)
        }
        getSum(100,200) //300
        getSum(100,200,300) //300
        getSum(100) //NaN
    </script>
</head>
<body>
</body>
</html>
箭頭函數(shù)的參數(shù)與參數(shù)默認(rèn)值
  • 箭頭函數(shù)中只有一個(gè)參數(shù)時(shí)面哥,可以省略();
  • 箭頭函數(shù)中代碼只有一行時(shí)毅待,可以省略{}尚卫;
  • 參數(shù)沒有傳入實(shí)參時(shí),參數(shù)默認(rèn)值才會生效尸红;
  <script>

    const fn = (a,b) => {
      console.log('a =',a);
      console.log('b =',b);
    }

    //省略() 與 {}
    const fn1 = a => console.log('a =',a);

    //參數(shù)默認(rèn)值
    const fn2 = (a,b,c = 30) => {
      console.log('a =',a);
      console.log('b =',b);
      console.log('c =',c);
    }

  </script>
Object對象作為函數(shù)參數(shù)
  • Object對象可以作為函數(shù)的參數(shù)袁稽;
  <script>

    function fn(a) {
      console.log('a =', a);
    }

    let obj = { name: 'yanzi' };
    fn(obj);

  </script>
  • Object對象字面量作為函數(shù)參數(shù)默認(rèn)值旁赊,函數(shù)多次調(diào)用,會多次創(chuàng)建新的Object對象字面量;
  • 自定義Object對象作為函數(shù)參數(shù)默認(rèn)值洗做,函數(shù)多次調(diào)用,都使用的同一個(gè)自定義Object對象巷怜;
  <script>

    function func(a = { name: '大神' }) {
      console.log(a.name);
      a.name = '小神';
      console.log(a.name);
    }
    func(); // 大神 小神
    func(); // 大神 小神

    let obj = { name: '大神' }
    function func1(a = obj) {
      console.log(a.name);
      a.name = '小神';
      console.log(a.name);
    }
    func1(); // 大神 小神
    func1(); // 小神 小神

  </script>
函數(shù)作為函數(shù)參數(shù)
  • 自定義函數(shù)呢袱,匿名函數(shù)表達(dá)式,箭頭函數(shù)作為函數(shù)的參數(shù)墩莫;
  <script>

    function func(a) {
      console.log('a =', a);
    }

    //自定義函數(shù)
    function func1() {
      console.log('自定義函數(shù)');
    }

    func(func1);

    //匿名函數(shù)表達(dá)式
    func(function () {
      console.log('匿名函數(shù)表達(dá)式');
    })

    //箭頭函數(shù)
    func(() => {
      console.log('箭頭函數(shù)');
    })

  </script>
arguments的使用
  • 當(dāng)我們不確定有多少個(gè)參數(shù)傳遞的時(shí)候芙委,可以用arguments來獲取,在JavaScript中狂秦,arguments實(shí)際上它是 當(dāng)前函數(shù)的一個(gè)內(nèi)置對象灌侣,所有函數(shù)都內(nèi)置了一個(gè)arguments對象,arguments對象中存儲了傳遞的所有實(shí)參裂问;
  • arguments展示形式是一個(gè)偽數(shù)組侧啼,因此可以遍歷,偽數(shù)組具有以下特點(diǎn):
    • 具有l(wèi)ength屬性堪簿;
    • 按索引方式儲存數(shù)據(jù)痊乾;
    • 不具有數(shù)組的push,pop等方法戴甩;
    <script>
        function fn() {
            console.log(arguments)
            console.log(arguments.length)
            console.log(arguments[2])
            for (var i = 0; i < arguments.length; i++) {
                console.log(arguments[i]); //10,2,33
            }
        }
        fn(10,2,33)
    </script>
函數(shù)返回值
  • 函數(shù)的返回值:只要函數(shù)遇到return 就把后面的結(jié)果 返回給函數(shù)的調(diào)用者符喝,且終止之后的代碼執(zhí)行,且只能返回一個(gè)值甜孤,是沒有返回值類型的协饲;
  • return什么都不返回 或者 不寫return畏腕,那么函數(shù)的返回值為undefined;
    <script>
        //定義聲明函數(shù)
        function getArrMax(arr) {
            var max = arr[0]
            for (var i = 1; i < arr.length; i++) {
                if (arr[i] > max) {
                    max = arr[i]
                }
            }
            return max
        }

        var max = getArrMax([100,200,30,40,50,333,234])
        console.log(max);
    </script>
    <script>
        function getResult(num1,num2) {
            //返回值為數(shù)組
            return [num1+num2,num1-num2]
        }
        var result = getResult(100,30)
        console.log(result);
    </script>
  • 箭頭函數(shù)只有一行代碼茉稠,且有返回值描馅,可省略return關(guān)鍵字 和 {};
  • 箭頭函數(shù)只有一行代碼而线,且返回值為Object對象字面量铭污,那么需要加上(Object對象字面量);
  <script>

    const func = (a, b) => {
      return a + b;
    }

    const func1 = (a, b) => a + b;

    const func2 = (a, b) => ({ name: 'yanzi' });

  </script>

JavaScript作用域

  • 全局作用域:整個(gè)script標(biāo)簽 或者是一個(gè)單獨(dú)的js文件膀篮;
  • 局部作用域:存在兩種分別為塊級作用域和函數(shù)作用域嘹狞;
    • 塊級作用域:在代碼塊執(zhí)行時(shí)創(chuàng)建,在代碼塊執(zhí)行完畢時(shí)銷毀誓竿;
    • 函數(shù)作用域:在函數(shù)調(diào)用時(shí)創(chuàng)建磅网,在函數(shù)調(diào)用完成時(shí)銷毀;
  • 全局變量:在全局作用域中的變量筷屡,注意在函數(shù)內(nèi)部 沒有聲明直接賦值的變量 也屬于全局變量涧偷,全局變量只有在瀏覽器關(guān)閉的時(shí)候才會被銷毀,比較占內(nèi)存資源毙死;
  • 局部變量:在局部作用域中的變量燎潮,當(dāng)函數(shù)執(zhí)行完成時(shí)就會被銷毀,比較節(jié)約內(nèi)存資源扼倘;
    <script>
        //全局作用域 全局變量
        var num = 100

        function addNum(a,b) {
            //局部作用域 局部變量
            var sum = 0
            sum = a + b
            //sum1沒有聲明直接賦值 屬于全局變量
            sum1 = 1000
            return sum
        }
        addNum(100,200)
    </script>
  • 作用域鏈:內(nèi)部函數(shù)訪問外部函數(shù)變量确封,JS解釋器會優(yōu)先在當(dāng)前對象中尋找目標(biāo)變量,若找到直接使用唉锌,若沒有找到隅肥,則去上一層作用域中去尋找,依次類推袄简,若找到全局作用域都沒有找到目標(biāo)變量,就會報(bào)錯(cuò)xxx is not defined泛啸,在作用域中尋找目標(biāo)變量绿语,遵循就近原則
  <script>

    var num = 10
    function func() { //外部函數(shù)
      var num = 20
      function func1() { //內(nèi)部函數(shù)
        console.log(num); // 20
      }
    }

    let a = '全局作用域中的a';
    {
      let a = '第一個(gè)塊級作用域中的a';
      {
        let a = '第二個(gè)塊級作用域中的a';
        console.log('a =', a);
      }
    }

  </script>

JavaScript預(yù)解析

  • JavaScript代碼是由瀏覽器中的JavaScript解析器來執(zhí)行的候址,JavaScript解析器在運(yùn)行JavaScript代碼的時(shí)候分為兩個(gè)步驟吕粹,分別為:預(yù)解析代碼執(zhí)行
  • 預(yù)解析:js引擎會將js里面所有的var和function 提升到當(dāng)前作用域的最前面岗仑;
  • 代碼執(zhí)行:按照代碼書寫的順序從上到下依次執(zhí)行匹耕;
  • 變量var的提升:將所有變量var聲明提升到當(dāng)前作用域的最前面,不會提升賦值操作荠雕;
  • 函數(shù)的提升:將所有函數(shù)聲明(定義)提升到當(dāng)前作用域的最前面稳其,不調(diào)用函數(shù)驶赏;
  • 變量let的提升:會將所有變量let聲明提升到當(dāng)前作用域的最前面,但是在賦值之前解釋器禁止對該變量的訪問既鞠;
  <script>

    console.log(a); //a is not defined
    a = 100;

    console.log(b); //undefined
    var b = 100;

    console.log(c); //Cannot access 'c' before initialization
    let c = 100;

  </script>
    <script>
        //第一種:
        console.log(num) //報(bào)錯(cuò)

        //第二種:
        console.log(num) //undefine
        var num = 10

        //第三種
        func() //111
        function func() {
            console.log(111)
        }

        //第四種
        fun() //報(bào)錯(cuò)
        var fun = function() {
            console.log(222)
        }
    </script>
  • 案例一:
    <script>
        var a = 18
        f1()

        function f1() {
            var b = 9
            console.log(a)
            console.log(b)
            var a = '123'
        }

        //上述代碼等價(jià)于
        var a
        function f1() {
            var b 
            var a
            b = 9
            console.log(a) //undefine 
            console.log(b) //9
            a = '123'
        }
        a = 18
        f1()
    </script>
  • 案例二:
    <script>
        f1()
        console.log(c)
        console.log(b)
        console.log(a)

        function f1() {
            // var a = 9; b = 9;c = 9 b與c沒有聲明 直接賦值 屬于全局變量
            // var a = b = c = 9 與 var a = 9,b = 9, c = 9 是不同的
            var a = b = c = 9
            console.log(a)
            console.log(b)
            console.log(c)
        }
        
        //以上代碼等價(jià)于
        function f1() {
            var a
            a = b = c = 9
            console.log(a) //9
            console.log(b) //9
            console.log(c) //9
        }

        f1()
        console.log(c) //9
        console.log(b) //9
        console.log(a) //報(bào)錯(cuò) a是局部變量
    </script>

立即執(zhí)行函數(shù)

  • 在開發(fā)中應(yīng)該盡量減少直接在全局作用域中編寫代碼煤傍,盡量在局部作用域中編寫代碼;
  • 立即執(zhí)行函數(shù):(function(){})()嘱蛋,是一個(gè)匿名函數(shù)蚯姆,直接執(zhí)行,且只會執(zhí)行一次洒敏;
  <script>

    (function(){
      console.log('立即執(zhí)行函數(shù)');
    })()

  <script>

函數(shù)中的this關(guān)鍵字

  • 函數(shù)在執(zhí)行時(shí)龄恋,JS解析器每次都會傳遞一個(gè)隱含參數(shù),此參數(shù)叫做this凶伙;
  • this會指向一個(gè)對象郭毕,其所指的對象會根據(jù)函數(shù)調(diào)用方式的不同而不同;
    • 以函數(shù)形式調(diào)用時(shí)镊靴,this指向的是window铣卡;
    • 以方法形式調(diào)用時(shí),this指向的是調(diào)用方法的對象偏竟;
  <script>

    function func() {
      console.log(this);
    }
    //以函數(shù)的形式調(diào)用時(shí)煮落,this指向window
    func(); //window

    //以方法的形式調(diào)用時(shí),this指向調(diào)用方法的對象
    const obj = { name: '小神' };
    obj.test = func;
    obj.test(); //obj

    const obj2 = { name: '大神', test: func };
    obj2.test(); //obj2

  </script>
  • 箭頭函數(shù)沒有自己的this踊谋,內(nèi)部打印的this是由外層作用域決定的蝉仇,等于外層作用域的this;
  • 箭頭函數(shù)的this和它的調(diào)用方式無關(guān)殖蚕;
<script>

    function func() {
      console.log(this);
    }
    //以函數(shù)的形式調(diào)用時(shí)轿衔,this指向window
    func(); //window

    //箭頭函數(shù)
    const func3 = (a, b) => {
      var num = a + b;
      console.log(this);
      return num;
    }
    func3(); //外層作用域?yàn)槿肿饔糜?this指向window

    const obj3 = {
      name: '如來',
      fn: func,
      fn2: func3,
      sayHello() {
        console.log(this.name);
        function t() {
          console.log('t -->', this);
        }
        t(); //window

        const t2 = () => {
          console.log('t2 -->', this);
        }
        t2(); //obj3
      }
    }
    obj3.fn();  //obj3
    obj3.fn2(); //window

    obj3.sayHello();

  </script>

高階函數(shù)

  • 高階函數(shù):若一個(gè)函數(shù)的參數(shù)或者返回值為函數(shù),那么這個(gè)函數(shù)就是高階函數(shù)睦疫;
  • 將函數(shù)作為參數(shù)害驹,就意味著可以對另一個(gè)函數(shù)動(dòng)態(tài)的傳遞代碼;
  <script>

    //高階函數(shù) -- 參數(shù)為一個(gè)函數(shù)
    function filter(arr, cb) {
      const newArr = [];
      for (let index = 0; index < arr.length; index++) {
        const element = arr[index];
        if (cb(arr[index])) {
          newArr.push(arr[index]);
        }
      }
      return newArr;
    }

    const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    var result = filter(arr, a => a % 2 === 0);
    console.log(result);

  </script>

閉包

  • 閉包:能訪問到外部函數(shù)作用域中變量的函數(shù)蛤育;
  • 構(gòu)成閉包的必要條件:
    • 存在函數(shù)的嵌套宛官;
    • 內(nèi)部函數(shù)要引用外部函數(shù)的變量;
    • 內(nèi)部函數(shù)要作為返回值瓦糕;
  <script>

    function outer() {
      let num = 0;
      return () => {
        num++;
        console.log(num);
      }
    }

    const newFn = outer;
    newFn();

  </script>
  • 函數(shù)的外層作用域(詞法作用域)底洗,在函數(shù)創(chuàng)建時(shí)就已經(jīng)確定了;
  • 閉包的實(shí)現(xiàn)原理就是利用了詞法作用域咕娄;
  <script>

    let a = '全局變量a';
    function fn() {
      console.log(a);
    }

    function fn2() {
      let a = 'fn2中的a';
      fn();
    }
    fn2();  //fn() --> 打印的是全局變量a 

    function fn3() {
      let a = 'fn3中的a';
      function fn4() {
        console.log(a);
      }
      return fn4;
    }

    let result = fn3();
    fn4(); //fn4() --> 打印的是fn3中的a
    
  </script>
  • 閉包的生命周期:
    • 閉包在外部函數(shù)調(diào)用時(shí)產(chǎn)生亥揖,外部函數(shù)每次調(diào)用都會產(chǎn)生一個(gè)全新的閉包;
    • 閉包在內(nèi)部函數(shù)回收時(shí)銷毀圣勒;
  • 閉包的注意事項(xiàng):
    • 閉包住要用來隱藏一些不希望外部訪問的內(nèi)容费变,這就意味著閉包需要占用一定的內(nèi)存空間摧扇;
    • 相比較于類Class來說,閉包比較浪費(fèi)內(nèi)存空間胡控,因?yàn)轭惪梢允褂迷投]包不可以扳剿;
  <script>

    function outer() {
      let someValue = 'some';
      return function () {
        console.log(someValue);
      }
    }

  </script>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市昼激,隨后出現(xiàn)的幾起案子庇绽,更是在濱河造成了極大的恐慌,老刑警劉巖橙困,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瞧掺,死亡現(xiàn)場離奇詭異,居然都是意外死亡凡傅,警方通過查閱死者的電腦和手機(jī)辟狈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來夏跷,“玉大人哼转,你說我怎么就攤上這事〔刍” “怎么了壹蔓?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長猫态。 經(jīng)常有香客問我佣蓉,道長,這世上最難降的妖魔是什么亲雪? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任勇凭,我火速辦了婚禮,結(jié)果婚禮上义辕,老公的妹妹穿的比我還像新娘虾标。我一直安慰自己,他們只是感情好灌砖,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布夺巩。 她就那樣靜靜地躺著,像睡著了一般周崭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上喳张,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天续镇,我揣著相機(jī)與錄音,去河邊找鬼销部。 笑死摸航,一個(gè)胖子當(dāng)著我的面吹牛制跟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播酱虎,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼雨膨,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了读串?” 一聲冷哼從身側(cè)響起聊记,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎恢暖,沒想到半個(gè)月后排监,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡杰捂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年舆床,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嫁佳。...
    茶點(diǎn)故事閱讀 40,030評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡挨队,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蒿往,到底是詐尸還是另有隱情盛垦,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布熄浓,位于F島的核電站情臭,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏赌蔑。R本人自食惡果不足惜俯在,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望娃惯。 院中可真熱鬧跷乐,春花似錦、人聲如沸趾浅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽皿哨。三九已至浅侨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間证膨,已是汗流浹背如输。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人不见。 一個(gè)月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓澳化,卻偏偏與公主長得像,于是被迫代替她去往敵國和親稳吮。 傳聞我的和親對象是個(gè)殘疾皇子缎谷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評論 2 355

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