JavaScript拡張(3)関数、this指向、コピー


JavaScriptプレミアム(3)
一、関数の定義と呼び出し
1、関数の定義方式
  • 関数の定義方法:1.関数宣言2.関数式3.Functionコンストラクタ
  • を使用
    //  1.     
            function fn(a, b) {
         
                return a + b;
            };
            //   2.      
            var fn1 = function() {
         
                console.log('123');
            };
            //   3.    Function     
            var fn2 = new Function('a', 'b', 'console.log(a+b)');
            fn2(1, 2);
            console.dir(fn2);
    

    2、関数呼び出し
     // 1.     
            function fn1() {
         
                console.log(123);
            }
            fn1();
            //   2.         
            var obj = {
         
                say: function() {
         
                    console.log('hello');
                }
            };
            obj.say();
            //   3.        
            function Student(name, age) {
         
                this.name = name;
                this.age = age;
            }
            var xm = new Student('  ', 12);
            console.log(xm);
            //   4.          
            var btn = document.querySelector('button');
            btn.addEventListener('click', function() {
         
                console.log('   ');
            });
            //   5.          
            window.setTimeout(function() {
         
                console.log('345');
            }, 1000);
            //   6.       
            var num = 10;
            !(function() {
         
                //           ,       
                var num = 10;
                console.log('hi');
            })()
    

    二、this指向
    1、関数内部thisの指向
  • 一般関数はwindow
  • を指す
  • オブジェクトの関数呼び出しは、呼び出し者
  • を指す.
  • コンストラクタの呼び出しは、インスタンスオブジェクト
  • を指す.
  • バインディングイベントの処理関数はバインディング者
  • を指す.
  • タイマの処理関数はwindow
  • を指す.
  • 直ちに関数を実行してwindow
  • を指します
    <div>hello</div>
        <button>  </button>
        <script>
            // 1.     
            function fn1() {
         
                console.log(this); //window
            }
            fn1();
            //   2.         
            var obj = {
         
                say: function() {
         
                    console.log('hello');
                }
            };
            obj.say(); //this   obj
            var foo = obj.say;
            foo(); //this  window
            //   3.        
            var that;
            function Student(name, age) {
         
                that = this;
                this.name = name;
                this.age = age;
            }
            var xm = new Student('  ', 12);
            console.log(xm);
            console.log(xm === that);
            //   4.          
            var btn = document.querySelector('button');
            btn.addEventListener('click', function() {
         
                console.log('   ');
                console.log(this); //  btn
            });
            //   5.          
            window.setTimeout(function() {
         
                console.log(this); //  window
            }, 1000);
            //   6.       
            var num = 10;
            !(function() {
         
                var num = 10;
                console.log(this); //  window
            })()
    

    2、callはthisの指向を変える
  • fn.call([thisArg, arg1, arg2…])
  • の役割:関数を呼び出し、呼び出し時のthisの値
  • を指定します.
  • パラメータ:thisArg関数のthisの指定値;arg 1,arg 2...オプションのパラメータリスト
  • 戻り値:関数呼び出しの結果
  • 注:最初のパラメータthisArgがnull、undefinedを渡さない場合、デフォルトの関数内thisはwindowを指します.
    3、apply方法
  • fn.apply(thisArg,[ argsArray])
  • の役割:関数を呼び出し、呼び出し時のthisの値
  • を指定します.
  • パラメータ:thisArg関数のthisの指定値;ArgsArrayオプションのパラメータ配列(!)
  • 戻り値:関数呼び出しの結果
  • callメソッドとの違い:
  • callメソッドは、2番目から各独立パラメータを伝達するが、applyはパラメータ配列
  • を伝達する.
  • callが使用可能な場合、
  • の代わりにapplyを使用することができる.
    function fn(a, b) {
         
                console.log(this);
                console.log(a, b);
            }
            fn.call({
         }, 2, 3);
            fn.apply({
         }, [2, 3]);
    
            // 1、  apply          
            var arr = [1, 254, 545, 12, 5];
            var res = Math.max.apply(null, arr);
            console.log(res);
    

    4、bind方法
  • var newFn = fn.bind(thisArg,arg1, arg2, …)
  • の役割:元の関数に基づいて新しい関数を作成します.この新しい関数のthisは最初のパラメータとして指定され、残りのパラメータは実パラメータとして新しい関数
  • に渡されます.
  • パラメータ:thisArgは新しい関数内のthisのプリセット値です.arg 1,arg 2は、新しい関数プリセットの入力パラメータ
  • である.
  • 戻り値:新しい関数(それ自体は関数を呼び出さない)
  •  function fn(a, b) {
         
                console.log(this);
                console.log(a + b);
            }
            // bind      ,                
            var newFn = fn.bind({
         }, 3, 4);
            newFn(); //     this      {},          ,bind     
            fn(3, 4) //     this     window
    

    三、厳格モード
    1、定義
    制限されたJavaScriptバリエーションの1つを採用し,従来の緩和モードから脱した.
    ​ 1. jsコードの不合理さと厳格さを解消し、奇妙な行為を減らす.
    ​ 2. コードの不安全な場所を排除し、コードの安全な運行を保証する
    ​ 3. コンパイラの効率化と実行速度の向上
    ​ 4.ECMAScriptの将来のバージョンで定義される可能性のあるいくつかの構文class extends superなどを無効にします.
  • 使用:scripコードブロックの一番前に「use strict」を追加する.

  • 2、厳格モードの変化
  • 変数宣言なしでは
  • を直接付与できません.
  • 変数は、
  • を使用する前に宣言する必要があります.
  • 関数内のthisデフォルトはundefined
  • を指します.
  • 非関数内のthisデフォルトはundefined
  • を指します.
  • 構造関数とクラスはnewを加えてのみ
  • を使用できます.
  • 関数のパラメータ名は、
  • に名前を変更できません.
  • 非関数のコードブロック内で関数を宣言することは許されない(chromeブラウザは実装されていない)
  • 四、高次関数
    他の関数を操作する関数には、主に2つのタイプの高次関数があります.
    ​ 1. 関数をパラメータとする関数
  • 関数を戻り値とする関数
  • 五、閉包
    1、定義:
    内部関数は外部関数が宣言する変数にアクセスします.この組み合わせは閉パッケージです.
    2、JSにおけるゴミ回収メカニズム(GC)
    ごみ回収メカニズムは、定期的に(周期的に)参照されなくなったメモリ(変数)を特定し、メモリを解放します.
    3、閉包の原因
    (1)1つの関数内の宣言された変数が他の関数に参照されていない場合、この関数を呼び出すと、すべての局所変数がゴミ回収メカニズムによって消去される.
    (2)この変数が別の関数に参照されると,この変数の値は常にメモリに保存され,ゴミ回収機構によって回収されず,閉パケットとなる.
    4、ケース:
  • 需要:各ボタンをクリックして現在のボタンのインデックス
  • をポップアップする
     <button>  </button>
        <button>  </button>
        <button>  </button>
        <button>  </button>
        <button>  </button>
    
        <script>
            //      :                   
            //   :                
            var btns = document.querySelectorAll('button');
            for (var i = 0; i < btns.length; i++) {
         
                btns[i].index = i;
                btns[i].onclick = function() {
         
                    console.log(this.index);
                }
            }
            //     
            for (var i = 0; i < btns.length; i++) {
         
                //              ,     
                (function(i) {
         
                    //   :   i              ,     
                    btns[i].onclick = function() {
         
                        console.log(i);
                    }
                })(i)
            }
    
  • タクシーの価格を計算する
  • タクシーの初乗り料金は8(3キロ以内)で、その後1キロごとに5元増加し、ユーザーはキロ数を入力すればタクシーの価格を出すことができ、渋滞があれば、前の価格に加えて10元の渋滞料金を徴収することができる.
    通常のタクシー価格と混雑時のタクシー価格を求めるオブジェクトをパッケージ化し、全体的に初乗り価格と総価格にアクセスできません.
     var obj = (function() {
         
                var total = 0;
                var start = 8;
                return {
         
                    price: function(km) {
         
                        total = km <= 3 ? start : (km - 3) * 5 + start;
                        return total;
                    },
                    busyPrice: function(isBusy) {
         
                        total = isBusy ? total + 10 : total;
                        return total;
                    }
                }
            })()
            console.log(obj.price(20));
            console.log(obj.busyPrice(true));
    

    六、再帰関数
  • 関数の内部で自分で自分を呼び出し、作用と循環効果は
  • に似ている.
  • 再帰は「スタックオーバーフロー」エラーが発生しやすいため、whileループと同様に割り込み条件
  • を追加する必要があります.
     //  n   m            5, 10 ===> 5 * 6 * ... * 10
     function fn2(n, m) {
         
                if (n == m) {
         
                    return m;
                }
                return n * fn2(n + 1, m);
            }
            var res2 = fn2(1, 5);
            console.log(res2);
    
    //              (    ) 1, 1, 2, 3, 5, 8, 13, 21...
    //          n                      
    
    function fn(n) {
         
                if (n == 0) return 0;
                if (n == 1) return 1;
                return fn(n - 1) + fn(n - 2);
                // return n==0||n==1?n:fn(n - 1) + fn(n - 2);
            }
            console.log(fn(4));
            //fn(3)+fn(2)==>fn(2)+fn(1)+fn(2)==>fn(1)+fn(0)+fn(1)+fn(2)==>fn(1)+fn(0)+fn(1)+fn(1)+fn(0)==>1+0+1+1+0=3
    

    七、コピー
    単純なデータ型はすべて直接コピーで、深浅コピーを区別しません
    1、浅いコピー
    オブジェクトの1レベルのデータのみをコピーし、複雑なデータ型はメモリアドレス値のみをコピーします(同じオブジェクトを参照).
    var obj = {
         
               id: 1,
               name: '  ',
               data: {
         
                   id: 2,
                   age: 19
               }
           }
           var obj2 = {
         };
           // obj2 = obj;
           //      
           for (var key in obj) {
         
               // obj2       
               obj2[key] = obj[key]
           }
           obj.data.id = 10; //     data    id ,obj2  id    
           //   data           
           console.log(obj2.data.id);
    

    2、深いコピー
  • オブジェクトの多層データをコピーします.複雑なデータ型に遭遇すると、新しいスペースが作成され続け、各レイヤのプロパティと値
  • がコピーされます.
  • と浅いコピーの違い
  • 深いコピーされたオブジェクトと元のオブジェクトは完全に分離されており、それぞれは互いに影響しないが、浅いコピーのすべての複雑なデータ型の値は、共通参照の
  • である.
    var obj1 = {
         
                id: 1,
                name: '  ',
                data: {
         
                    id: 2,
                    age: 19,
                    goods: {
         
                        id: 10
                    }
                },
                arr: [1, 2, 3]
            }
            var obj2 = {
         }
            function deepClone(obj1, obj2) {
         
                //     
                for (var key in obj1) {
         
                    var temp = obj1[key];
    
                    //        
                    //   ==> obj2     ,    
                    if (Array.isArray(temp)) {
         
                        obj2[key] = [];
                        deepClone(temp, obj2[key]);
                        //   ==> obj2     ,    
                    } else if (temp instanceof Object) {
         
                        obj2 = {
         };
                        deepClone(temp, obj2[key]);
                        //       ,     obj2 
                    } else {
         
                        obj2[key] = temp;
                    }
                }
            };
            deepClone(obj1, obj2);
    
            obj1.data.id = 10;
            obj1.arr[0] = 100;
    
            // obj2        obj1          ,     
            console.log(obj2.data.id);
            console.log(obj2.arr[0]);
    

    3、補充
  • 浅いコピーで
  • を迅速に実現
     var obj = {
         
                a: 1,
                b: 2,
                c: {
         
                    d: 4
                }
            }
            obj.c.d = 10;
            var newObj = {
         ...obj
            };
            console.log(newObj);
    
    
  • 深いコピーは
  • を迅速に実現する.
  • JSON.parse(JSON.stringify(obj))
  • JSON.stringify()は、複雑なデータ型を文字列
  • に変換することができる.
  • JSON.parse()は文字列をオブジェクト
  • に再変換することができる.
     var obj2 = JSON.parse(JSON.stringify(obj))
            obj.c.d = 20;
            console.log(obj2); //obj2