JS進階(三)(函數進階,apply,閉包...等)

--------------記錄自JS高級視頻中的學習筆記包括了函數進階,apply,call,bind方法的使用,閉包,遞歸,淺拷貝與深拷貝,沙箱.數組

函數進階

  • 建立函數的兩種方式:
    • 函數聲明:
      function f1(){
          console.log("函數聲明");
      }
      f1();
      複製代碼
      • 函數聲明若是放在if-else語句中,在IE8中會出錯(函數聲明會被提高)
    • 函數表達式:
      var f2 = function(){
          console.log("函數表達式");
      };
      f2(); 
      複製代碼
      • 使用函數表達式更好
  • 函數中的this指向
    • 普通模式
      • 普通函數中的this---window
      • 定時器中的this---window
      • 構造函數中的this---實例對象
      • 對象.方法中的this---當前的實例對象
      • 原型方法中的this---實例對象
    • 嚴格模式: "use strict"
      • 普通函數中的this得在window.函數();調用的時候纔是window,若函數();則爲undefined
  • 函數的不一樣的調用方式
    • 普通函數:
      function f1(){}
      f1();
      複製代碼
    • 構造函數---經過new來調用,建立對象
      function F1(){}
      var f = new F1();
      複製代碼
    • 對象的方法
      function F1(){
          this.f1 = function(){};
      }
      var f = new F1();
      f.f1(); 
      複製代碼
  • 函數也是對象,對象不必定是函數
    • 對象中有__proto__原型,是對象
    • 函數中有prototype原型,是對象
    • 若一個東西里有prototype,又有__proto__,說明是函數也是對象;(Math對象中有__proto__但沒有prototype)
    • 全部的函數實際上都是Function的構造函數建立出來的實例對象
  • 函數中的幾個成員介紹
    • name屬性: 函數的名字,name的屬性是隻讀的,不能修改
    • arguments屬性: 實參的個數
    • length屬性: 函數定義的時候形參的個數
    • caller屬性: 調用者(例如f1函數在f2函數中調用的,此時的調用者就是f2)
  • 函數能夠做爲參數使用
    • 函數做爲參數的時候,若是是命名函數,那麼只傳入命名函數的名字,沒有括號
    • 練習:
    var arr = [1, 100, 20, 200, 40, 50, 120, 10];
        //排序---函數做爲參數使用,匿名函數做爲sort方法的參數使用,那麼此時的匿名函數中有兩個參數,
        arr.sort(function (obj1,obj2) {
          if(obj1>obj2){
            return -1;
          }else if(obj1==obj2){
            return 0;
          }else{
            return 1;
          }
        });//從小到大排序
    複製代碼
  • 判斷數據類型
    • typeof: 返回一個字符串,表示未經計算的操做數的類型
    • 對象 instanceof Object: 返回布爾類型,判斷對象是否爲某個類型
    • Object.prototype.toString(): 返回Object的數據類型([object Object])
    • Object.prototype.toString().call(對象): 返回的是該對象的數據類型
    • 獲取某個對象的類型是否是傳入的類型
    //獲取某個對象的類型是否是你傳入的類型
        //[10,20,30] 是否是"[object Array]"
        //type---是變量----是參數----"[object Array]"
        //obj---是變量-----是參數----[10,20,30];
    
        //判斷這個對象和傳入的類型是否是同一個類型
        function getFunc(type) {
          return function (obj) {
            return Object.prototype.toString.call(obj) === type;
          }
        }
    
        var ff = getFunc("[object Array]");
        var result = ff([10, 20, 30]);
        console.log(result);//true
    
        var ff1 = getFunc("[object Object]");
        var dt = new Date();
        var result1 = ff1(dt);
        console.log(result1);//false
    複製代碼

apply,call與bind的使用

apply,call
  • 做用: 均可以改變this的指向
    function f1(x,y){
            console.log((x + y) + ":===>" + this);
        }
        //apply 和 call調用
        f1.apply(null,[1,2]);//3:===>[object Window]
        f1.call(null,1,2);//3:===>[object Window]
        //改變this的指向
        var obj = {
        
        };
        f1.apply(obj,[1,2]);//3:===>[object Object]
        f1.call(obj,1,2);//3:===>[object Object]
    複製代碼
  • apply的使用語法
    • 函數名字.apply(對象,[參數1,參數2,...]);
    • 方法名字.apply(對象,[參數1,參數2,...]);
  • call的使用語法
    • 函數名字.call(對象,參數1,參數2,...);
    • 方法名字.call(對象,參數1,參數2,...);
  • 不一樣的地方 : 參數傳遞的方式不同
  • 只要是想使用別的對象的方法,而且但願這個方法是當前對象的,那麼就可使用apply或者是call的方法改變this的指向
  • apply和call方法實際上並不在函數這個實例對象中,而是在Function的prototype中
bind方法
  • bing方法是複製的意思,參數能夠在複製的時候傳進去,也能夠在複製以後調用的時候傳入進去
    function A(age){
            this.age = age;
        }
        A.prototype.a = function(){
            console.log(this + "===>" + this.age);
        }
        function B(age){
            this.age = age;
        }
        var aObj = new A(10);
        var bObj = new B(20);
        //複製了一份
        var c = aObj.a.bind(bObj);
        c();//[object Object]===>20
    複製代碼
    • 使用語法:
      • 函數名字.bind(對象,參數1,參數2,...)--->返回值是複製以後的這個函數
      • 方法名字.bind(對象,參數1,參數2,...)--->返回值是複製以後的這個方法
    • 應用實例
      function ShowRandom(){
              //1-10的隨機數
              this.number = parseInt(Math.random()*10 + 1);
          }
          //添加原型方法
          ShowRandom.prototype.show1 = function(){
              //改變了定時器中的this的指向了,原本應該是window,如今爲實例對象
              window.setInterval(this.show2.bind(this),1000);
          };
          //添加原型方法
          ShowRandom.prototype.show2 = function(){
              //顯示隨機數--
              console.log(this.number);
          };
          //實例對象
          var sr = new ShowRandom();
          //調用方法,輸出隨機數字
          //調用這個方法一次,能夠不停的產生隨機數字
          sr.show1();
      複製代碼
做用域和做用域鏈及預解析
  • 變量--->局部變量和全局變量
    • 局部變量是在函數中,函數使用結束後,局部變量就會被自動的釋放,閉包後,裏面的局部變量的使用做用域鏈被自動的延長
  • 做用域:就是變量的做用範圍--->局部做用域和全局做用域
    • js中沒有塊級做用域--一對括號中定義的變量,這個變量能夠在大括號外面使用,函數中定義的變量是局部變量
  • 做用域鏈:變量的使用,從裏向外,層層的搜索,搜索到了就能夠直接使用了
    • 層層搜索,搜索到0級做用域的時候,若是仍是沒有找到這個變量,結果就是報錯
  • 預解析:就是在瀏覽器解析代碼以前,把變量的聲明和函數的聲明提早(提高)到該做用域的頂部(函數聲明優先於變量聲明)
閉包
  • 閉包的概念:函數A中,有一個函數B,函數B中能夠訪問函數A中定
    • 模式:函數模式的閉包,對象模式的閉包
    • 做用:緩存數據,延長做用域鏈
    • 優勢和缺點:緩存數據(沒有及時的釋放 )
    • 應用:
      //函數模式的閉包
          function f1(){
              var num = 10;
              //函數的聲明
              function f2(){
                  console.log(num);
              }
              //函數調用
              f2();
          }
          f1();//10
              
          //對象模式的閉包:函數中有一個對象
          function f3(){
              var num = 10;
              var obj = {
                  age : num 
              }
              console.log(obj.age);//10
          }
      複製代碼
    • 案例:產生三個相同的隨機數
      function f1(){
              var num = parseInt(Math.random()*10 + 1);
              return function (){
                  console.log(num);
              }
          }
          
          var ff = f1();
          ff();//3(隨機的)
          ff();//3
          ff();//3
      複製代碼
    • 總結:若是想要緩存數據,就把這個數據放在外層的函數和裏層的函數的中間位

沙箱

  • 沙箱: 環境,黑盒,在一個虛擬的環境中模擬真實世界,作實驗,實驗結果和真實世界的結果是同樣的,可是不會影響真實世界
  • 沙箱--小環境
    (function (){
            var num = 10;
            console.log(num + 10);
        }());
    複製代碼
  • 小案例
    (function (){
            var str = "小白喜歡小黑";
            str = str.substr(2);
            console.log(str);//喜歡小黑
        }());
        
        (function (){
            var str = "小明喜歡小紅";
            str = str.substr(2);
            console.log(str);//喜歡小紅
        }());
    複製代碼
  • 總結:放在沙箱內(自調用函數)能夠避免全局變量衝突,不會污染外部環境

遞歸

  • 遞歸: 函數中調用函數本身,此時就是遞歸,遞歸必定要有結束的條件,通常應用在遍歷上,遞歸輕易不要用,效率很低
  • 案例: 求n個數字的累加和
    //遞歸實現:求n個數字的和
        //函數的聲明
        function getSum(x){
            if(x==1){
                return 1;
            }
            return x + getSum(x - 1); 
        }
        //函數的調用
        console.log(getSum(5));//15
        /*
        *
        * 執行過程:
        * 代碼執行getSum(5)--->進入函數,此時的x是5,執行的是5+getSum(4),此時代碼等待
        * 此時5+getSum(4),代碼先不進行計算,先執行getSum(4),進入函數,執行的是4+getSum(3),等待, 先執行的是getSum(3),進入函數,執行3+getSum(2),等待,先執行getSum(2),進入函數,執行 2+getSum(1);等待, 先執行getSum(1),執行的是x==1的判斷,return 1,因此,
        * 此時getSum(1)的結果是1,開始向外走出去
        * 2+getSum(1) 此時的結果是:2+1
        * 執行:
        * getSum(2)---->2+1
        * 3+getSum(2) 此時的結果是3+2+1
        * 4+getSum(3) 此時的結果是4+3+2+1
        * 5+getSum(4) 此時的結果是5+4+3+2+1
        *
        * 結果:15
        *
        *
        *
        * */
    複製代碼
  • 案例: 求一個數字各個位數上的數字的和: 123 --->6
    function getEverySum(x){
            if(x<10){
                return x;
            }
            //獲取的是這個數的個位數
            return x%10 + getEverySum(parseInt(x/10));
        }
        console.log(getEverySum(1364));//14
    複製代碼
  • 案例: 求斐波那契數列(第三個數等於前兩個數之和 第一第二個數爲1)
    function getFib(x){
            if(x==1 || x==2){
                return 1;
            }
            return getFib(x-1) + getFib(x-2);
        }
        console.log(getFib(12));//144
    複製代碼

淺拷貝和深拷貝

  • 淺拷貝: 拷貝結束複製,就至關於把一個對象中的全部的內容,複製一份給另外一個對象,直接複製,或者說,就是把一個對象的地址給了另外一個對象,他們指向相同,兩個對象之間有共同的屬性或者方法,均可以使用
    //做用:把一個對象的屬性複製到另外一個對象中,淺拷貝
        //把a對象中的全部的屬性複製到對象b中
        function extend(a,b){
            for(var key in a){
                b[key] = a[key];
            }
        }
    複製代碼
  • 深拷貝: 拷貝函數複製,深: 把一個對象中的全部屬性或者方法,一個一個的找到,而且在另外一個對象中開闢相應的空間,一個個的存儲到另外一個對象中
    //經過函數實現,把對象a中全部的數據深拷貝到對象b中
        function extend(a,b){
            for(var key in a){
                //先獲取a對象中每一個屬性的值
                var item = a[key];
                //判斷這個值是否是數組
                if(item instanceof Array){
                    //若是是數組,那麼在b對象中添加一個新的屬性,而且這個屬性值也是數組
                    b[key] = [];
                    //調用這個方法,把a對象中的這個數組的屬性值一個個的複製到b對象的這個數組屬性中
                    extend(item,b[key]);
                }else if(item instanceof Object){//判斷在這個值是否是對象類型的
               //若是是對象類型的,那麼在b對象中添加一個屬性,是一個空對象
               b[key] = {};
               //再次調用這個函數,把a對象中的屬性對象的值一個一個複製到b對象的這個屬性對象中
               extend(item,b[key]);
                }else{
                    //若是值是普通的數據,直接複製到b對象整過屬性中
                    b[key] = item;
                }
            }
        }
    複製代碼
相關文章
相關標籤/搜索