簡単なシミュレーションでJavascriptのcall、appy、bind方法を実現します.

6222 ワード

目次
  • ガイド
  • 暗黙的な損失
  • ハードバインド
  • 実装および原理解析
  • 全体実装(純版/コメントなし)
  • は最後に書いてあります.
    引子優待券サイトwww.cs.3 cn
    「あなたが知らないJavaScript--上巻」の中でthisに関する紹介と深い章節を読み終わった後、thisに対する指向についてこの文章で簡単にまとめました.続いて、thisに関する知識を利用して、javascriptでよく使われているcall、appy、bindの方法をシミュレーションして実現してもいいですか?
    そこで本文があって、雑談は多く全文を言わないで始まります!
    陰でなくします
    アナログ実装では暗黙的に使われることがありますので、ここで紹介します.
    暗黙的な損失とは、暗黙的に結合された関数がバインディングされたオブジェクトをなくし、最終的にデフォルトのバインディングに適用されるという共通のthisバインディング問題である.本来は暗黙的バインディング(obj.xxx thisはObjを指す)の場合、最終的にデフォルトバインディング(thisはグローバルオブジェクトを指す)を適用する.
    よくある暗黙的な損失状況1:引用伝達
    var a = 'window'
    function foo() {
        console.log(this.a)
    }
    var obj = {
        a: 'obj',
        foo: foo
    }
    
    obj.foo() // 'obj'    this => obj
    var lose = obj.foo
    lose()  // 'window'    this => window
    
    一般的な暗黙的な損失の場合2:コールバック関数として伝えられます.
    var a = 'window'
    function foo() {
        console.log(this.a)
    }
    var obj = {
        a: 'obj',
        foo: foo
    }
    
    function lose(callback) {
        callback()
    }
    
    lose(obj.foo)  // 'window'    this => window
    
    
    // ================        ===============
    var t = 'window'
    function bar() {
        console.log(this.t)
    }
    
    setTimeout(bar, 1000)   // 'window'
    
    これについて私がまとめた考え(正しいかどうかは分かりません):明示的なバインディングを排除した後、どのように値伝達をしても、最後には何の修飾もない呼び出しがある限り、デフォルトのバインディングに適用されます.
    さらに全体的な実現の鍵となる原理が得られます.いくら値が伝達されても、最終的な呼び出し方法でthisの方向が決定されます.
    ハードバインディング
    直観的な説明ハードバインディングとは、一度関数を明示的に指定したら、thisが終わったら、その後どうやって呼び出しても、そのthisの方向はもう変えられないということです.
    ハードバインディングの実現は陰的な損失による問題を解決しました.bind関数の実現利用はハードバインディングの原理です.
    //       
    var a = 'window'
    function foo() {
        console.log(this.a)
    }
    var obj = {
        a: 'obj',
        foo: foo
    }
    
    function lose(callback) {
        callback()
    }
    
    lose(obj.foo)   // 'window'
    
    var fixTheProblem = obj.foo.bind(obj)
    lose(fixTheProblem) // 'obj'
    
    実現及び原理分析
    アナログ実現call
    //     call
    Function.prototype._call = function ($this, ...parms) {     // ...parms   rest   ,                          
        /* 
            this      _call            this       
            **         **  =>                   $this(context)   
        */
        $this['caller'] = this
        //$this['caller'](...parms)
    
        //               
        $this.caller(...parms) // ...parms   spread   ,               caller      
        /* 
                 ,                  
                1. $this.caller      this      
                2.         $this.caller  ,            
                3.   this    $this
        */
        delete $this['caller']  //                        ,           
    }
    
    アナログ実装
    //     apply  **  _call       ,            /    **
    Function.prototype._apply = function ($this, parmsArr) {    //     apply               
        $this['caller'] = this
        $this['caller'](...parmsArr) // ...parmsArr   spread   ,               caller      
        delete $this['caller']
    }
    
    からにはコールとコールapplyの前の類似度(結合度)がこんなに高いなら、私達はさらにそれら(同じコード)を引き離すことができます.
    function interface4CallAndApply(caller, $this, parmsOrParmArr) {
        $this['caller'] = caller
        $this['caller'](...parmsOrParmArr)
        delete $this['caller']
    }
    
    
    Function.prototype._call = function ($this, ...parms) {
        var funcCaller = this
        interface4CallAndApply(funcCaller, $this, parms)
    }
    
    
    Function.prototype._apply = function ($this, parmsArr) {
        var funcCaller = this
        interface4CallAndApply(funcCaller, $this, parmsArr)
    }
    
    一つはよく展示できると思います.コールとコールapply実現原理の例
    var myName = 'window'
    var obj = {
        myName: 'Fitz',
        sayName() {
            console.log(this.myName)
        }
    }
    
    var foo = obj.sayName
    
    var bar = {
        myName: 'bar',
        foo
    }
    
    bar.foo()
    
    アナログ実現ビッド
    //            bind
    Function.prototype._bind = function ($this, ...parms) {
        $bindCaller = this  //     _bind          :        
        //     bind      :      
        return function () { //  rest     arguments        
            return $bindCaller._apply($this, parms)
        }
    }
    
    ハードバインディングの原理を示すことができる例
    function hardBind(fn) {
        var caller = this
        var parms = [].slice.call(arguments, 1)
        return function bound() {
            parms = [...parms, ...arguments]
            fn.apply(caller, parms) // apply                 
        }
    }
    
    
    var myName = 'window'
    function foo() {
        console.log(this.myName)
    }
    var obj = {
        myName: 'obj',
        foo: foo,
        hardBind: hardBind
    }
    
    //      
    foo()   // 'window'
    obj.foo()   // 'obj'
    
    var hb = hardBind(foo)
    //                        this  
    hb()    // 'window'
    obj.hb = hb //  obj         
    obj.hb()    // 'window'
    
    //        
    var hb2 = obj.hardBind(foo)
    hb2()   // 'obj'    //     this    window
    
    全体的な実現(純版/コメントなし)
    function interface4CallAndApply(caller, $this, parmsOrParmArr) {
        $this['caller'] = caller
        $this['caller'](...parmsOrParmArr)
        delete $this['caller']
    }
    
    
    Function.prototype._call = function ($this, ...parms) {
        var funcCaller = this
        interface4CallAndApply(funcCaller, $this, parms)
    }
    
    
    Function.prototype._apply = function ($this, parmsArr) {
        var funcCaller = this
        interface4CallAndApply(funcCaller, $this, parmsArr)
    }
    
    
    Function.prototype._bind = function ($this, ...parms) {
        $bindCaller = this
        return function () {
            return $bindCaller._apply($this, parms)
        }
    }
    
    
    
    // ============    ===============
    var foo = {
        name: 'foo',
        sayHello: function (a, b) {
            console.log(`hello, get the parms => ${a} and ${b}`)
        }
    }
    
    var bar = {
        name: 'bar'
    }
    
    foo.sayHello._call(bar, 'Fitz', 'smart')
    foo.sayHello._apply(bar, ['Fitz', 'smart'])
    
    var baz = foo.sayHello._bind(bar, 'Fitz', 'smart')
    baz()
    
    var testHardBind = foo.sayHello._bind(bar, 'hard', 'bind')
    testHardBind._call(Object.create(null))   // hello, get the parms => hard and bind   _bind    
    
    最後に書く
    私はただ前端の白を勉強しています.間違っているところがあります.よろしくお願いします.
    もしあなたにインスピレーションや助けがあると感じたら、メッセージや関心をお願いします.ありがとうございます.