zoukankan      html  css  js  c++  java
  • 手把手教你实现三种绑定方式(call、apply、bind)

    关于绑定首先要说下this的指向问题。

    我们都知道:

    函数调用时this指向window

    对象调用函数时this指向对象本身

    看下面得例子:

    // 1
    function test(){
        const name = 'test1';
        console.log(this.name)
    }
    constname = 'test2'
    test() // test2
    // 当前函数调用指向了window,所以打印的为test2
    
    // 2
    var obj = {
        name:'test1',
        get(){
            console.log(this.name)
        }
    }
    var name = 'test2';
    obj.get() // test1
    // 当前通过对象调用。this指向obj,打印test1
    
    // 3
    var obj = {
        name:'test1',
        get(){
            console.log(this.name)
        }
    }
    var name = 'test2';
    var fn = obj.get;
    fn() // test2
    // 将obj.get作为值赋值给fn,执行fn,这时候相当于通过函数执行,this重新指向

    那如何将this指向到我们指定的对象中呢?

    js提供改变this指向的三种方法:call、apply、bind

    其中call和apply执行改变this的同时会执行函数,bind是会返回一个函数。

    而call与apply的区别在于参数,call可以传多个参数,而apply传一个数组作为参数。下面来看看实现:

    模拟实现call

    Function.prototype.myCall = function(thisArg, ...argumentArr){
        if(typeof this !== 'function') {
            throw new TypeError(`${this} is not function`)
        };
        if(thisArg === undefined || thisArg === null){
            thisArg = window;
        }
        thisArg['fn'] = this;
      // 通过对象执行函数将this指向该对象
    var result = thisArg['fn'](...argumentArr) delete thisArg['fn']; return result } var obj = { name:'test1', get(data1, data2){ console.log(this.name, data1, data2) } } obj.get.myCall({name: 'test2'}, 1, 2, 3) // test2,1,2 // 原理就是通过传入的新的对象来执行这个函数,就是通过对象调用函数的方式

    模拟实现apply

    Function.prototype.myApply = function(thisArg, argumentArr){
        if(typeof this !== 'function') {
            throw new TypeError(`${this} is not function`)
        };
        if(thisArg === undefined || thisArg === null){
            thisArg = window;
        }
        if(argumentArr === undefined || argumentArr === null){
            argumentArr = []
        }
        thisArg['fn'] = this;
        var result = thisArg['fn'](...argumentArr)
        delete thisArg['fn'];
        return result
    }
    
    var obj = {
        name:'test1',
        get(data1, data2){
            console.log(this.name, data1, data2)
        }
    }
    obj.get.myApply({name: 'test2'}, [1, 2, 3]) // test2,1,2
    
    // 我们发现与call唯一的不同就是入参,call使用rest 参数,将多余的参数整合成argument形式,而apply入参直接是数组

    模拟实现bind

    Function.prototype.myBind = function(thisArg, ...argumentArr){
        if(typeof this !== 'function') {
            throw new TypeError(`${this} is not function`)
        };
        if(thisArg === undefined || thisArg === null){
            thisArg = window;
        }
        var self = this
        var bindfn = function(){
            return self.call(thisArg, ...argumentArr)
        }
        bindfn.prototype = self.prototype
        return bindfn
    }
    
    var obj = {
        name:'test1',
        get(data1, data2){
            console.log(this.name, data1, data2)
        }
    }
    const fn = obj.get.myBind({name: 'test2'}, 1, 2, 3)
    fn() //  test2,1,2
    
    // 相比于前两个bind会有不一样,bind使用到了闭包,
    // 我们之前知道函数执行this是指向window,但是这里我们执行却指向了我们的目标对象,实现这样的方式就是闭包

    如果我们没有赋值操作执行var self = this,而是直接使用this执行的话

    this.call(thisArg, ...argumentArr)
    

    这个时候this是指向window的,这个时候会报this.call is not a function当我们进行赋值给self ,那么这个时候self 就形成了返回的函数fn的专属背包而不被销毁,每当我们执行fn的时候取到的this都是obj.get方法

  • 相关阅读:
    我的Java学习路线图
    请求重定向和请求转发的区别
    PHP代码审计学习-php安全基础
    无密码正向直连内网linux目标机复现
    Windows API 学习
    Http请求走私
    免杀手法-tcp套字节传递shellcode学习
    自启动模块构造-计划任务
    自启动模块构造-快速启动目录
    进程注入免杀学习
  • 原文地址:https://www.cnblogs.com/smallpen/p/14864815.html
Copyright © 2011-2022 走看看