JavaScriptの動特性(eval,call,appyとbindによって表現される)

13337 ワード

JavaScriptの動特性(eval,call,appyとbindによって表現される)
JavaScriptはオブジェクト指向、関数式、動的プログラミング言語に基づいています.今はブラウザとサーバー端末で使えるようになりました.
ここでは対象に向けたものではなく、関数式プログラミングについても言及せず、単にダイナミック性を議論します.ダイナミックとは何ですか?
言語の動態とは、プログラムが実行される時にその構造を変えることができるということです.
通俗的に言って、実行していません.このコードがどんな状況になるかは分かりません.ある変数と声明の時は違っています.ある関数の作用範囲が変わったかもしれません.ダイナミックな特性があれば、このコードの実行プロセスは経験によってしか判断できない場合が多いです.
JavaScriptのダイナミック性は以下のいくつかの関数でまとめられると思います.
  • eval
  • appyとcall
  • bind
  • 1.eval関数
    eval(alert("       !"));  // -->       !
    alert(window.eval === eval);   // -->true
    alert(eval in window);         // -->false
    
    ここでは使い方が分かります.evalはwindowオブジェクトの下にマウントされた関数で、evalは です.
    eval関数のダイナミック性は、シナリオ実行時に動的に何かを変えることができる.
    上記の例はこの点を表しています.eval() はプログラム実行時に何かを動的に変えることができます.
    次はeval関数のもう一つの比較的お父さんの問題について説明します.eval は栗を挙げます.
    var i = 100;
    function myFunc() {
        var i = "text";
        window.eval('i = "hello"');
        alert(i);  //        text,IE6-8  hello
    }
    myFunc();
    alert(i); //        hello,IE6-8  100
    
    どうしてですか?原因は JS eval です.ここで私たちが指定したwindow.eval は、iの値をハロー文字列に変更することを意図しています.しかし、別のブラウザJS解析カーネルのエヴァ関数に対する作用領域の設定は異なり、IE 6-8はJScript を使用しているので、evalはiをmyFunnc関数のvar i = "text" iと読んで、myFun関数の中のtextをハローに変更した後にハローが表示されます.現代のブラウザは、window.evalが変化したのはグローバルi=100の値だと考えている.window.evalevalに変更したら?
    var i = 100;
    function myFunc() {
        var i = "text";
        eval('i = "hello"'); 
    }
    myFunc();
    alert(i); // -->100
    
    おめでとうございますここのevalは、window作用領域を指定していないので、ブラウザが一括して100を出力する.
    eval関数のデフォルトの変更は、現在のスコープの変数値です.
    よくあるブラウザJSエンジンとカーネルのリストを添付します.
    会社
    ブラウザ
    JSエンジン
    レンダリングエンジン
    Microsoft
    IE 6-8
    JScrtp
    Trident
     
    IE 9-11
    Chakra
    Trident
     
    Edge
    Chakra
    Edge
    Mozia
    Firefox
    Jager Monkey
    Gecko
    Google
    Chrome
    V 8
    Blink
    Apple
    Safari
    Webkit
    スクエアFish Extreme
    Opera
    Opera 12.16+
    Blink
    カーキマン
    これらはJSエンジンとカーネルの一部です.他のバージョンは自分で検索してください.
    2.appyとcall
    2.1 appyとcallの基本的な使い方
    アプリとコールの使用はよく似ています.クリを挙げます.
    var name = "JaminQian",
        obj = {
            name: "ManfredHu"
        };
    
    function myFunc() {
        alert(this.name);
    }
    myFunc();         // -->JaminQian
    myFunc.call(obj); // -->ManfredHu
    
    ここの役割はthisの方向を変えることです.thisは実は違う環境での指し示しが違っていることを知っています.場合によってはwindowグローバルオブジェクトであり、あるオブジェクトであり、appyとcallを通じて、関数の中のthisの指向を任意に変えることができます.
    この例を見てください.
    function Animal(){    
        this.name = "Animal";
        this.args = arguments; //             
        this.showName = function(){    
            console.log(this.name);
        };
        this.getArgsNum = function(){
            console.log(this.args);
        }
    }    
    
    function Cat(num1,num2,num3){    
        Animal.apply(this,arguments); //  Animal
        this.name = "Cat";
    }
    
    function PersianCat(){ //   
        Cat.apply(this,arguments); //  Cat
        this.name = "PersianCat";
    }
    
    var animal      = new Animal();    
    var cat         = new Cat(1,2,3);
    var PersianCat  = new PersianCat([1,"2",[3]]);
    
    //  this.name
    animal.showName(); //-->Animal
    animal.showName.call(cat); //-->Cat
    animal.showName.call(PersianCat); //-->PersianCat
    
    //         
    animal.getArgsNum();    //-->[]
    cat.getArgsNum();       //-->[1,2,3]
    PersianCat.getArgsNum();//-->[[1,"2",[3]]]
    
    ここの生物の鎖はAnimal->Cat->PersianCat( )で、生物学のはよく分かりませんか?その後は、callを用いて、構造関数で父類の属性を継承していますが、自分の特殊な属性nameもあり、オブジェクト指向の継承と多状態を模倣して実現しました.
    最後に、applyの最も一般的な方法であり、パラメータを他の関数に無留保で伝達する.
    2.2 appyとcallの実用的な使い方
    2.2.1配列の最大値、最小値を取得する
    もしJSで配列の最大値の最小値を求める方法を使えば、遍歴を思い出すかもしれません.規則的かどうか聞いて、半分割でアルゴリズムを探します.でもここの使い方は微妙です.
    var numbers = [5,"30",-1,6, //         ,numbers[1]      "30"
            {   
                a:20, //             ,   valueOf  
                valueOf:function() {
                    return 40
                }
            },
    ];
    //         
    var max = Math.max.apply(Math,numbers),
        min = Math.min.call(Math,-10,2,6,10);
    console.log(max); //-->40
    console.log(min); //-->-10
    
    JSは非常に怠惰であることを知っています.文字列が必要なときだけ、Object.prototype.toString()メソッドを呼び出して文字列に変換します.数値が必要なときは、Object.prototype.valueOf()メソッドを呼び出して数字に変換します.ここではvalueOfを用いて文字列"30"を数値30に変換する.もちろん全部が数字ならもっと簡単ですが、ここでは詳しく説明しません.
    2.2.2元の配列に項目を追加する
    二つの配列を統合するにはどうすればいいですか?Array.prototype.concat()方法を思いつくことができます.
    var arr1 = [22, 'foo', {
        age: "21"
    }, -2046];
    var arr2 = ["do", 55, 100];
    var arr3 = arr1.concat(arr2);
    console.log(arr3); //-->[22, "foo", Object, -2046, "do", 55, 100]
    
    OK合併が完了したら、アール2を使ってpushの各項目をアール1に回す方法を考えるかもしれません.優雅な配列の結合方法は?犬の血の脚本はきっと書きます.
    var arr1 = [22, 'foo', {
        age: "21"
    }, -2046];
    var arr2 = ["do", 55, 100];
    Array.prototype.push.apply(arr1,arr2); //       apply,      
    console.log(arr1); //-->[22, "foo", Object, -2046, "do", 55, 100]
    
    四両千斤の足を引っ張るものがありますか?
    2.2.3認証配列タイプ
    ある日、BOSSはABの同僚のコードを再構成して効率を上げるようにします.重複した部分については必ず抽象的に出てきます.うん、両方とも検出配列の動作があります.自然です.isArray関数を実装して判断します.それから太ももをたたきますと、また原生の判断のisArrayの方法があるのではありませんか?はい、探してみましたが、お父さんの問題を発見しました.IE 9++にはArray.isArray()方法があります.OKです.互換性があればいいじゃないですか?
    function isArray(value) {
        if(typeof Array.isArray === "function") { //ES5           ,IE9+  
            return Array.isArray(value);
        } else {
            return Object.prototype.toString.call(value) === "[object Array]";
        }
    }
    
    論理は非常に単純で粗暴であり、以下の互換性のある方法をよく見てください.原理はObject.prototype.toString()を呼び出したときに"[object Array]"文字列を返します.もちろん、ここでは、タイプ検出は、基本タイプ検出用typeofが十分であり、numberstringbooleanundefinedtypofのいずれもinstanceofで検出することができる.ユーザー定義の参照タイプについては、Object.prototype.hasOwnPropertyおよびconstructorまたはiframe属性でも十分である.エラーが発生しやすいところは、検出配列と検出関数の2つの場所、特にtypeof foo === "function"のところで、元の検出方法は無効になりますので、特に注意してください.検出配列は上記のように、比較的に公認されている方法である.検出関数はslice(fooが関数であると仮定する)で検出される.
    2.2.4類配列用配列の方法
    クラスの配列は何ですか?興味のあるものは前の文章を訳して、クラスの配列やarray-likeを探してください.ここで一番多く使われているのは、jQueryと推定されます.jQueryのソースコードの使い方を抽象してみてください.あるいはLookに行ってもいいです.中国語の注釈版のjQueryのソースコードがあります.下のコードは運行できません.理解を深めるだけです.
    var arr = [];
    var slice = arr.slice; //   slice  
    toArray: function() {
        return slice.call( this ); //                         
    },
    
    クラス配列を配列に変換する方法は、2つにすぎない.1つはconcat、1つはon.
    3.bind関数
    3.1 jQueryの中のbind方法
    bindというと、ここの本編のテーマですが、bindとは何ですか?古いバージョンのjQueryを使っている方が多いと、よくこのように書くかもしれません.
    $( "#foo" ).bind( "click", function() {
        alert( "User clicked on 'foo.'" );
    });
    
    idfooの要素バインディングclickイベントであり、匿名のコールバック関数であることを意味する.もちろんさまざまなタイプのイベントを結びつけることもできます.
    $( "#foo" ).bind( "mouseenter mouseleave", function() {
        $( this ).toggleClass( "entered" );
    });
    
    もっと詳しい使い方はjQuery公式サイトの.bind()のAPIを参照してください.
    3.2原生JavaScriptにおけるbind方法
    もう一つは、オリジナルのbind関数であり、ECMAScript 5にはFunction.prototypeにいくつかのオリジナルの拡張方法が追加されており、Function.prototype.bindが含まれている.信じないなら、Googleやフォックスの下で下記のコードを実行してみてください.IEは比較的に馬鹿です.IE 9+はbind方法をサポートします.
    console.log(Function.prototype.bind); //-->bind() { [native code] }
    
    旧式ブラウザのBInd互換性のある方法(MDNから):
    if (!Function.prototype.bind) {
        Function.prototype.bind = function (oThis) {
            if (typeof this !== "function") { //                
                throw new TypeError("Function.prototype.bind() error");
            }
    
            var aArgs = Array.prototype.slice.call(arguments, 1), 
                fToBind = this, //  this,            
                fNOP = function () {},
                fBound = function () {
                    //               ,                   
                    return fToBind.apply(this instanceof fNOP 
                                        && oThis ? this : oThis 
                                        || window,
                                   aArgs.concat(Array.prototype.slice.call(arguments)));
                };
    
            fNOP.prototype = this.prototype;
            fBound.prototype = new fNOP();
    
            return fBound;
        };
    }
    
    以下、JS原生bindの基本的な使い方を見てみましょう.
    function foo() {
        console.log(this.name);
        console.log(arguments);
    }
    var obj = {
        name: 'ManfredHu'
    }
    
    // foo  obj    ,               
    var newFunc = foo.bind(obj, '    1', '    2'); 
    newFunc(); 
    
    //output:(       )
    //ManfredHu
    //Arguments[2]   0: "    1"  1: "    2"
    
    そして、実は使い方も簡単です.原理は簡単に言います.bindは元の関数copyを一つにまとめ、copyコピーの文脈を結び付けました.もちろん、ここの文脈はthisの指しを表しています.そして、後は変えたくても変えられません.
    var obj = {};
    function foo() {
        return this;
    }
    var foo2 = foo.bind(obj); //         
    var obj2 = {};
    obj2.foo2 = foo2;
    
    console.log(obj === foo2());        //-->true
    console.log(obj === window.foo2()); //-->true
    console.log(obj === obj2.foo2());   //-->true
    
    ここでは、関数実行のコンテキストをwindowおよびobj2で変更しようとしても、成功しませんでした.
    次は終わりです.高エネルギーです.ある日ぶらぶらしている時に面白い訳文を見ました.最初に読んでみましたが、よく分からないところがあって、他のことをするために急いでいました.先に置いておきました.後ろに暇があったら、この訳文を見つけました.あるいはコードです.灰色の常に鋭いです.役割も書き方もいたるところにJSの動態特性を徹底的に表しています.
    var context = { foo: "bar" };
    
    function returnFoo () { //  this.foo     
        return this.foo;
    }
    
    returnFoo(); //-->undefined(  window.foo   )
    
    var bound = returnFoo.bind(context); // bind       
    
    bound(); //-->"bar"(            ,    context.foo)
    
    returnFoo.call(context);  //--> bar(call     )
    
    returnFoo.apply(context); //--> bar
    
    context.returnFoo = returnFoo; //       context  
    
    context.returnFoo(); //--> bar(returnFoo     this context)
    
    //-----------------------------------------------------------------------   
    //           ,          ,              
    //-----------------------------------------------------------------------
    
    [1,2,3].slice(0,1);  //-->[1](       ,       )
    
    var slice = Array.prototype.slice; //      ,     slice       ,      
    
    //         ,slice           
    slice(0, 1); //--> TypeError: can't convert undefined to object
    
    //  ,           ,slice           
    slice([1,2,3], 0, 1); //--> TypeError: ...
    
    //      ,    [1,2,3].slice(0,1);  ,  slice        
    slice.call([1,2,3], 0, 1); //--> [1]
    
    //      ,     apply     ,          
    slice.apply([1,2,3], [0,1]); //--> [1]
    
    //     ,                ,       “  ”,    
    //   slice.call     slice      
    //                  ,        JS      ,      
    //   slice    , call    ,          ,      ,        
    slice = Function.prototype.call.bind(Array.prototype.slice);
    
    //    slice.call([1,2,3], 0, 1);         call   slice    
    slice([1,2,3], 0, 1); //--> [1]
    
    //              ,bind.call   bind   
    var bind = Function.prototype.call.bind(Function.prototype.bind);
    
    //OK,       ,slice bind        
    
    //       
    var context = { foo: "bar" };
    function returnFoo () {
        return this.foo;
    }
    
    //        "bind"  
    //bind(function,context)
    //@function          
    //@context        
    //@return                
    //             :returnFoo.bind(context,[args1,args2……])
    //            ?        ?
    var amazing = bind(returnFoo, context);
    amazing(); // --> bar
    
    4.まとめ
  • bindとcallおよびappyは、関数実行のコンテキストを動的に変えることができ、JavaScriptの動的特性
  • をよく表していると言えます.
  • JavaScriptの動特性は上のeval()、call/appy、bind()これらの
  • だけではありません.
  • これらのものを使ってみても、JSという言語がよりよく理解できます.コードが優雅になり、コードが多重される確率も高くなります.
    5.引用参考:
    MDN公式文書——Function.prototype.bind()張小俊128——Javascript中のBind,CallとAppley