国产成人精品18p,天天干成人网,无码专区狠狠躁天天躁,美女脱精光隐私扒开免费观看

JavaScript中call、apply、bind實(shí)現原理詳解

發(fā)布時(shí)間:2021-08-17 12:16 來(lái)源: 閱讀:0 作者:ClyingDeng 欄目: JavaScript 歡迎投稿:712375056

目錄

        前言

        眾所周知 call、apply、bind 的作用都是‘改變'作用域,但是網(wǎng)上對這這‘改變'說(shuō)得含糊其辭,并未做詳細說(shuō)明,‘改變'是直接替換作用域?誰(shuí)替換誰(shuí)?怎么產(chǎn)生效果?這些問(wèn)題如果不理解清楚,就算看過(guò)手寫(xiě)實(shí)現,估計也記不長(cháng)久

        所以本文介紹了call、apply、bind的用法和他們各自的實(shí)現原理。

        call

        call() 方法使用一個(gè)指定的 this 值和單獨給出的一個(gè)或多個(gè)參數來(lái)調用一個(gè)函數。

        即:可以改變當前函數的this指向;還會(huì )讓當前函數執行。

        用法

        function fun() {
          console.log(this.name, arguments)
        }
        let obj = { name: 'clying' }
        fun.call(obj, 'deng', 'deng')
        // clying [Arguments] { '0': 'deng', '1': 'deng' }
        

        實(shí)現

        call和apply的實(shí)現,都是使用將函數放到字面量obj的某個(gè)屬性中,使函數中的this指向obj這個(gè)字面量對象。

        簡(jiǎn)單的實(shí)現版本:

        Function.prototype.mycall = function (context) {
          context = (context == null || context == undefined) ? window : new Object(context)
          context.fn = this
          context.fn()
          delete context.fn
        }
        

        給函數原型添加mycall方法,創(chuàng )建一個(gè)上下文對象context,如果傳入的對象不存在時(shí),將指向全局window。通過(guò)給context添加fn屬性,context的fn引用調用該方法的函數fun,并執行fun。執行完成之后刪除該屬性fn。

        當中需要先獲取傳入的參數,那它變成字符串數組。

        執行方法使用的是eval函數,再通過(guò)eval計算字符串,并執行其中代碼,返回計算結果。

        升級版:

        給call中傳入參數。

        Function.prototype.mycall = function (context) {
          context = (context == null || context == undefined) ? window : new Object(context)
          context.fn = this
          let arr = []
          for (let i = 1; i < arguments.length; i++) {
            arr.push('argument[' + i + ']') //  ["arguments[1]", "arguments[2]"]
          }
          let r = eval('context.fn(' + arr + ')') // 執行函數fun,并傳入參數
          delete context.fn
          return r
        }
        

        此外,也可以通過(guò)解構的語(yǔ)法來(lái)實(shí)現call。

        Function.prototype.mycall = function (context, ...args) {
          context = (context == null || context == undefined) ? window : new Object(context)
          context.fn = this
          context.fn(...args)
          delete context.fn
        }
        

        如果想要能夠多次調用call方法,可以將context.fn(...args)保存到變量中,最后返回即可。

        Function.prototype.mycall = function (context, ...args) {
          context = (context == null || context == undefined) ? window : new Object(context)
          context.fn = this
          let r = context.fn(...args)
          delete context.fn
          return r
        }
        

        apply

        與call方法類(lèi)似,call方法接收的是一個(gè)參數列表,而apply方法接收的是一個(gè)包含多個(gè)參數的數組。

        用法

        將函數中的this指向傳入的第一個(gè)參數,第二個(gè)參數為數組。

        function fun() {
          console.log(this.name, arguments);
        }
        let obj = {
          name: 'clying'
        }
        fun.apply(obj, [22, 1])
        // clying Arguments(2) [22, 1]
        

        實(shí)現

        自己實(shí)現一個(gè)apply方法myapply。實(shí)現方法與call類(lèi)似,不過(guò)在接收參數時(shí),可以使用一個(gè)args作為傳入的第二個(gè)參數。直接判斷如果未傳入第二個(gè)參數,直接執行函數;否則使用eval執行函數。

        Function.prototype.myapply = function (context, args) {
         context = (context == null || context == undefined) ? window : new Object(context)
          context.fn = this
          if(!args) return context.fn()
          let r = eval('context.fn('+args+')')
          delete context.fn
          return r
        }
        

        bind

        bind() 方法創(chuàng )建一個(gè)新的函數,不自動(dòng)執行,需要手動(dòng)調用bind() 。這個(gè)新函數的 this 被指定為 bind() 的第一個(gè)參數,而其余參數將作為新函數的參數,供調用時(shí)使用。

        用法

        將obj綁定到fun函數的this上,函數fun可以使用obj內部的屬性,和傳入的變量。

        function fun() {
          console.log(this.name, arguments);
        }
        let obj = {
          name: 'clying'
        }
        let b = fun.bind(obj,2)
        b(3)
        // clying Arguments(2) [2, 3]
        

        此外,bind方法綁定的函數還可以new一個(gè)實(shí)例,不過(guò)此時(shí)的this會(huì )發(fā)生改變。

        升級版-使用原型屬性用法:

        function fun() {
          console.log(this.name, arguments);
        }
        let obj = {
          name: 'clying'
        }
        fun.prototype.age = 23
        let b = fun.bind(obj, 3)
        let instance = new b(4)
        console.log(instance.age);
        //undefined Arguments(2) [3, 4]
        // 23
        

        實(shí)現

        基本版:

        bind的實(shí)現可以基于call和apply的基礎上實(shí)現。

        因為bind不是立即執行的,所以可以通過(guò)返回一個(gè)函數,讓用戶(hù)手動(dòng)執行。在返回函數中利用call或者apply傳入指定的this對象和參數。

        apply實(shí)現bind

        Function.prototype.mybind = function (context) {
          let that = this
          let bindargs = Array.prototype.slice.call(arguments, 1)
          return function () {
            let args = Array.prototype.slice.call(arguments)
            return that.apply(context, bindargs.concat(args))
          }
        }
        

        利用apply方法,主要是在獲取處理bind傳入的參數,以及用戶(hù)執行函數傳入的參數。利用Array原型方法的slice方法,截取所需的參數。

        在獲取bind傳入的參數時(shí),需要從第二個(gè)參數開(kāi)始截取,所以開(kāi)始位置為1。

        call實(shí)現bind

        Function.prototype.mybind = function (context, ...args1) {
          let that = this
          return function (...args2) {
            return that.call(context, ...args1, ...args2)
          }
        }
        

        call實(shí)現直接將參數拼接call方法的后面即可。

        升級版:

        bind除了可以改變this指向、用戶(hù)可以在bind后面傳入參數也可以在用戶(hù)執行時(shí)傳入參數外。還可以讓執行函數進(jìn)行new操作。

        當一個(gè)綁定函數是用來(lái)構建一個(gè)值的,原來(lái)提供的 this 就會(huì )被忽略。不過(guò)提供的參數列表仍然會(huì )插入到構造函數調用時(shí)的參數列表之前。

        apply

        Function.prototype.mybind = function (context) {
          let that = this
          let bindargs = Array.prototype.slice.call(arguments, 1)
          function fBind() {
            let args = Array.prototype.slice.call(arguments)
            // 如果使用的是new,那么this會(huì )指向fBind實(shí)例,this作為當前實(shí)例傳入 不是的話(huà),使用context上下文對象
            return that.apply(this instanceof fBind ? this : context, bindargs.concat(args))
          }
          return fBind
        }
        

        在使用new操作符時(shí),注意的是需要改變this的指向問(wèn)題,如果是new,那么this指向的是實(shí)例,不使用new則指向bind當前傳入的第一個(gè)參數。

        此外,還牽扯到原函數可以添加自身方法屬性。如果想要能夠使用fun自身的原型方法還需要使用fBind.prototype = this.prototype,實(shí)現原型共用。但是對于引用類(lèi)型屬性值共享,不能在不改變其他實(shí)例情況下改變(一個(gè)原型方法或屬性改變,所有引用的都會(huì )發(fā)生改變)。

        Function.prototype.mybind = function (context) {
          let that = this
          let args = Array.prototype.slice.call(arguments, 1)
          function fBind() { // 執行bind函數
            let bindargs = Array.prototype.slice.call(arguments)
            return that.apply(this instanceof fBind ? this : context, args.concat(bindargs))
          }
          function Fn(){} // 兩個(gè)類(lèi)的原型并未公用,而是通過(guò)原型鏈的方式找到該原型方法
          Fn.prototype = this.prototype
          fBind.prototype = new Fn()
          return fBind
        }
        

        對于上述情況,可以使用一個(gè)函數中間件的形式,利用原型鏈去找到原函數原型方法或屬性。

        call

        call與apply的差別只是處理參數的不同,其他均類(lèi)似。

        Function.prototype.mybind = function (context, ...args1) {
          let that = this
          function fBind(...args2) {
            return that.call(this instanceof fBind ? this : context, ...args1, ...args2)
          }
          function Fn() { }
          Fn.prototype = this.prototype
          fBind.prototype = new Fn()
          return fBind
        }
        

        總結

        到此這篇關(guān)于JavaScript中call、apply、bind實(shí)現原理的文章就介紹到這了,更多相關(guān)call、apply、bind原理內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

        免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自本網(wǎng)站內容采集于網(wǎng)絡(luò )互聯(lián)網(wǎng)轉載等其它媒體和分享為主,內容觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如侵犯了原作者的版權,請告知一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容,聯(lián)系我們QQ:712375056,同時(shí)歡迎投稿傳遞力量。

        亚洲国产精品无码久久一线| 亚洲AV无码乱码国产麻豆穿越| 免费国产黄线在线播放| 久久精品无码专区免费青青| 国产H视频在线观看| 波多野结衣在线观看一码|