1. 程式人生 > >JavaScript學習記錄十七

JavaScript學習記錄十七

apply和call方法的使用

    //apply和call的使用     //作用:可以改變this的指向

    //apply和call方法中如果沒有傳入引數,或者是傳入的是null,那麼呼叫該方法的函式物件中的this就是預設的window

    *JavaScript中存在繼承,通過呼叫apply或call傳過去的第一個引數來動態指定方法中的 this .

    function f1(x,y) {
      console.log("結果是:"+(x+y)+this);
      return "10000";
    }
    f1.apply();
    f1.call();

    f1.apply(null);
    f1.call(null);

apply和call都可以讓函式或者方法來呼叫,傳入引數和函式自己呼叫的寫法不一樣,但是效果是一樣的
    f1.apply(null,[100,200]);
    f1.call(null,100,200);

apply和call的使用方法     /*     * apply的使用語法     * 函式名字.apply(物件,[引數1,引數2,...]);     * 方法名字.apply(物件,[引數1,引數2,...]);     * call的使用語法     * 函式名字.call(物件,引數1,引數2,...);     * 方法名字.call(物件,引數1,引數2,...);     *     * 作用:改變this的指向     * 不同的地方:引數傳遞的方式是不一樣的

    * 只要是想使用別的物件的方法,並且希望這個方法是當前物件的,那麼就可以使用apply或者是call的方法改變this的指向

        function f1() {
            console.log(this+":====>呼叫了");
        }
        f1是函式,f1也是物件,因為f1中有 __proto__和prototype
        console.dir(f1);
        物件呼叫方法,說明,該物件中有這個方法
        f1.apply();
        f1.call();
        console.log(f1.__proto__==Function.prototype);
        所有的函式都是Function的例項物件
        console.log(Function.prototype);//ƒ () { [native code] }
        console.dir(Function);
        apply和call方法實際上並不在函式這個例項物件中,而是在Function的prototype中
bind是用來複制一份
//使用的語法:
/*理解:將改變this後的函式狀態複製了一份。函式呼叫之後內部this使用的是指定後的
* 函式名字.bind(物件,引數1,引數2,...);---->返回值是複製之後的這個函式
* 方法名字.bind(物件,引數1,引數2,...);---->返回值是複製之後的這個方法
    function Person(age) {
      this.age=age;
    }
    Person.prototype.play=function () {
      console.log(this+"====>"+this.age);
    };

    function Student(age) {
      this.age=age;
    }
    var per=new Person(10);
    var stu=new Student(20);
    //複製了一份
    var ff=per.play.bind(stu);
    ff();  [object Object]====>20

bind方法的使用

  <script>
    通過物件,呼叫方法,產生隨機數
    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();
  </script>

函式中的幾個成員

    //函式中有一個name屬性----->函式的名字,name屬性是隻讀的,不能修改     //函式中有一個arguments屬性--->實參的個數     //函式中有一個length屬性---->函式定義的時候形參的個數     //函式中有一個caller屬性---->呼叫(f1函式在f2函式中呼叫的,所以,此時呼叫者就是f2)

    function f1(x,y) {
      console.log(f1.name);
      console.log(f1.arguments.length);
      console.log(f1.length);
      console.log(f1.caller);//呼叫者
    }

函式還可以通過引數傳遞來實現呼叫,在函式體中執行函式

           function f1(fn) {
             setInterval(function () {
               console.log("定時器開始");
               fn();
               console.log("定時器結束");
             },1000);
           }

           f1(function () {
             console.log("好睏啊,好累啊,就是想睡覺");
           });

函式作為返回值

           可以參考java中設定toString方法進行理解
           var num=10;
           console.log(typeof num);  number
           var obj={};
           console.log(obj instanceof Object);  true
           更改this為obj,直接列印obj的使用會顯示其資料型別
           Object.prototype.toString.call(obj);
            console.log(obj)  {}

            獲取資料型別,修改this指向具體型別資料,通過toString方法進行列印
           console.log(Object.prototype.toString.call([]));  [object Array]
           console.log(Object.prototype.toString.call(new Date()));  [object Date]

           函式作為返回值的使用
        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

函式作為引數練習,有點像java中的重寫equals方法

    *sort方法會從零號位置進行遍歷和比較,如果返回值大於零,則進行交換。

    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;
      }
    });
    console.log(arr);

    var arr1=["acdef","abcd","bcedf","bced"];
    arr1.sort(function (a,b) {
      if(a>b){
        return 1;
      }else if(a==b){
        return 0;
      }else{
        return -1;
      }
    });
    console.log(arr1);

案例:函式作為引數

    function File(name, size, time) {
      this.name = name;//電影名字
      this.size = size;//電影大小
      this.time = time;//電影的上映時間
    }
    var f1 = new File("jack.avi", "400M", "1997-12-12");
    var f2 = new File("tom.avi", "200M", "2017-12-12");
    var f3 = new File("xiaosu.avi", "800M", "2010-12-12");
    var arr = [f1, f2, f3];

    function fn(attr) {
      //函式作為返回值
      return function getSort(obj1, obj2) {
        if (obj1[attr] > obj2[attr]) {
          return 1;
        } else if (obj1[attr] == obj2[attr]) {
          return 0;
        } else {
          return -1;
        }
      }
    }

    var ff = fn("name");
    //函式作為引數
    arr.sort(ff);
    for (var i = 0; i < arr.length; i++) {
      console.log(arr[i].name + "====>" + arr[i].size + "===>" + arr[i].time);
    }

作用域和作用域鏈和預解析

    //變數---->區域性變數和全域性變數,     //作用域:就是變數的使用範圍     //區域性作用域和全域性作用域     //js中沒有塊級作用域---一對括號中定義的變數,這個變數可以在大括號外面使用     //函式中定義的變數是區域性變數

        if(true){
            var num=10;
        }
        console.log(num)    10
        function fn() {
            var num2=20;
        }
        console.log(num2);  報異常 num2 is not defined

    作用域鏈:變數的使用,從裡向外,層層的搜尋,搜尋到了就可以直接使用了層層搜尋,搜尋到0級作用域的時候,如果還是沒有找到這個變數,結果就是報錯

    *如果裡層沒有對應的變數,它會一層一層想外查詢

        var num=10;
        function f() {
            var num=20;
            function f1() {
                var num=30;
                console.log(num);
            }
            f1();
        }
        f();

    //預解析:就是在瀏覽器解析程式碼之前,把變數的  宣告  和  函式的宣告  提前(提升)到該作用域的最上面

        console.log(num);
        var num=10;   undefined 宣告提前了,但是沒有初始化
        f();   函式執行了,預解析函式提前了
        function f() {
            console.log('函式執行了')
        }
        console.log(f1); undefined 函式的宣告提前了
        var f1=function() {
            console.log('宣告提前了')
        }

閉包     * 閉包的概念:函式A中,有一個函式B,函式B中可以訪問函式A中定義的變數或者是資料,此時形成了閉包(這句話暫時不嚴謹)     * 閉包的模式:函式模式的閉包,物件模式的閉包     * 閉包的作用:快取資料,延長作用域鏈     * 閉包的優點和缺點:快取資料

        函式模式的閉包:在一個函式中有一個函式
           function f1() {
             var num=10;
             //函式的宣告
             function f2() {
               console.log(num);  10
             }
             //函式呼叫
             f2();
           }
           f1();

        物件模式的閉包:函式中有一個物件
           function f3() {
             var num=10;
             var obj={
               age:num
             };
             console.log(obj.age);//10
           }
           f3();

閉包小練習

        function f() {
            var num=10;
            return function(){
                num++;
                return num;
            }
        }
        var fn=f();
        console.log(fn()); 11
        console.log(fn()); 12
        console.log(fn()); 13

總結:如果想要快取資料,就把這個資料放在外層的函式和裡層的函式的中間位置

    //閉包的作用:快取資料.優點也是缺陷,沒有及時的釋放

    //區域性變數是在函式中,函式使用結束後,區域性變數就會被自動的釋放     //閉包後,裡面的區域性變數的使用作用域鏈就會被延長

    function showRandom() {
      var num=parseInt(Math.random()*10+1);
      console.log(num);
    }

    showRandom();
    showRandom();
    showRandom();

實現了快取功能的閉包,多次呼叫列印的數字都一樣
    function f1() {
      var num=parseInt(Math.random()*10+1);
      return function () {
        console.log(num);
      }
    }

    var ff=f1();

    ff();
    ff();
    ff();

案例:點贊小案例

  <style>
    ul {
      list-style-type: none;
    }

    li {
      float: left;
      margin-left: 10px;
    }

    img {
      width: 200px;
      height: 180px;
    }

    input {
      margin-left: 30%;
    }
  </style>
  <script>
    //$永遠都是24k純帥的十八歲的楊哥$
  </script>
</head>
<body>
<ul>
  <li><img src="images/ly.jpg" alt=""><br/><input type="button" value="贊(1)"></li>
  ...若干個...
</ul>
<script>

  //獲取所有的按鈕
  //根據標籤名字獲取元素
  function my$(tagName) {
    return document.getElementsByTagName(tagName);
  }
  //閉包快取資料
  function getValue() {
    var value=2;
    return function () {
      //每一次點選的時候,都應該改變當前點選按鈕的value值
      this.value="贊("+(value++)+")";
    }
  }
  //獲取所有的按鈕
  var btnObjs=my$("input");
  //迴圈遍歷每個按鈕,註冊點選事件
  for(var i=0;i<btnObjs.length;i++){
    //註冊事件
    btnObjs[i].onclick=getValue();
  }

       沙箱:環境,黑盒,在一個虛擬的環境中模擬真實世界,做實驗,實驗結果和真實世界的結果是一樣,但是不會影響真實世界

    var num=10;
    (function () {
        var num=20;
        console.log(num+10);  30
    })();

沙箱小案例

    (function () {
        document.getElementById('btn').onclick=function () {
            console.log('按鈕被點選了')
        }
    })();
    (function () {
        var str='我是中國人';
        console.log(str.substr(2))   中國人
    })();

沙箱小案例

<div>這是div</div>
<div>這是div</div>
<p>這是p</p>
<p>這是p</p>
<script>
  var getTag = 10;
  var dvObjs = 20;
  var pObjs = 30;
  (function () {
    //根據標籤名字獲取元素
    function getTag(tagName) {
      return document.getElementsByTagName(tagName)
    }
    //獲取所有的div
    var dvObjs = getTag("div");
    for (var i = 0; i < dvObjs.length; i++) {
      dvObjs[i].style.border = "2px solid pink";
    }
    //獲取所有的p
    var pObjs = getTag("p");
    for (var i = 0; i < pObjs.length; i++) {
      pObjs[i].style.border = "2px solid pink";
    }
  }());
  console.log(getTag);  10
  console.log(dvObjs);  20
  console.log(pObjs);   20

遞迴:函式中呼叫函式自己,此時就是遞迴,遞迴一定要有結束的條件

        function f(n) {
            if(n==1){
                return 1;
            }
            return n+f(n-1);
        }
        console.log(f(10));