• 模拟实现js的bind方法


    var obj = {};
    console.log(obj);
    console.log(typeof Function.prototype.bind); // function
    console.log(typeof Function.prototype.bind());  // function
    console.log(Function.prototype.bind.name);  // bind
    console.log(Function.prototype.bind().name);  // bound
    

    bind是什么

    • a. bindFunction原型链中Function.prototype的一个函数,每个函数都可以调用它。

    • b. bind本身是一个函数名为bind的函数,返回值也是函数,函数名是bound。(console出来为

      bound 加上一个空格)。

    let obj = {
        name: "yato"
    }
    
    function original(a,b){
        console.log(this.name)
        console.log([a,b])
        return false
    }
    
    let bound = original.bind(obj, 1)
    let boundInvoke = bound(2)                    // 'yato', Array(2)[1,2]
    
    console.log(boundInvoke)                       // false
    console.log(original.bind.name)                // bind
    console.log(original.bind.length)              // 1
    console.log(original.bind().length)            // 2 返回original函数形参个数
    console.log(bound.name)                        // 'bound original'
    console.log((function(){}).bind().name)        // 'bound '
    console.log((function(){}).bind().length)      // 0
    

    进一步理解bind

    • a. 调用bind的函数中的this指向bind()函数的第一个参数。

    • b. 函数bind()时传递的参数被bind接受处理,bind()完毕之后,程序调用返回的函数(bound)时,传递的参数也接收处理了,也就是在bind()内部合并处理了。

    • c. 并且bind()后的函数的name为bound+空格+调用bind的函数名。如果调用函数为匿名函数,则名字为bound+空格

    • d. bind后的返回值函数,执行后返回值时原函数(original)的返回值(上例中的false)

    • e. bind函数的形参(即函数的length)是1bind后返回的bound函数形参不定,根据绑定的函数原函数(original)形参个数决定。

    根据上面的两个例子,模拟实现一个简单版的bindFn

    Function.prototype.bindFn = function bindFake(thisArg){
        if(typeof this !== 'function'){
            throw new TypeError(this + 'must be a function')
        }
    
        // 存储函数本身
        let self  = this
        
        // 去除thisArg的其他参数,转成数组
        let args = [].slice.call(arguments, 1)
        let bound = function(){
            // bind 返回的函数,也就是bound,在程序中被调用时传递的参数转成数组
            let boundArg = [].slice.call(arguments);
    
            // apply修改this指向,把两个函数的参数合并传给self函数,返回执行结果
            return self.apply(thisArg, args.concat(boundArg))
        }
    
        return bound
    }
    
    // Test
    let obj = {
        name: 'yato'
    }
    
    function original(a, b){
        console.log(this.name)
        console.log([a,b])
    }
    
    let bound = original.bindFn(obj, 1)
    bound(2);  // 'yato', [1,2]
    

    但是函数是可以使用new来实例化的。

     let obj = {name : 'yato'}
    
     function original(a, b){
         console.log('this : ', this)
         console.log('typeof this : ', typeof this)
         this.name = b
         console.log('name: ', this.name)
         console.log('this: ', this)
         console.log([a,b])
     }
    
     let bound = original.bind(obj, 1)
     let newBoundInvoke = new bound(2)
     console.log('newBoundInvoke: ', newBoundInvoke)
    // this :  original {}
    // typeof this :  object
    // name:  2
    // this:  original { name: 2 }
    // [ 1, 2 ]
    // newBoundInvoke:  original { name: 2 }
    

    分析例子可以得出结论

    • a. 从例子中可以看出this指向了new bound()生成的对象

    • b. new bound() 的返回值是以original原函数构造器生成的新对象。original原函数的this指向的就是这个新对象。

    • c.简要剖析下new做了什么

      1. 创建一个全新的空对象
      2. 对这个对象指向原型链接(instance.__proto__ = Class.prototype ),其实Class.prototype就是constructor
      3. 生成的新对象会绑定到函数调用的this
      4. 通过new创建的每个对象最终被[[prototype]]链接这个函数的prototype上(参考2)
      5. 如果函数没有返回对象类型Object(包含Function, Array, Date, RegExg, Error),那么new表达式中的函数调用会自动返回这个新的对象
    所以相当于在new调用时,bind的返回值函数bound内部要实现new的操作
    // 第二版 实现new调用
    Function.prototype.bindFn = function bindFake(thisArg){
        if(typeof this !== 'function'){
            throw new TypeError(this + ' must be a function')
        }
    
        // 存储调用bind的函数本身的引用
        let self = this
    
        // 去除thisArg参数,其他转成数组
        let args = [].slice.call(arguments, 1)
        let bound = function(){
            let boundArgs = [].slice.call(arguments)
            let finalArgs = args.concat(boundArgs)
    
            // new 调用时,其实this instanceof bound 判断不是很准确。es6
            // new.target就是解决这一问题的
            if(this instanceof bound){
                // 这里是实现上文描述的 new 的第 1, 2, 4 步
                // 1.创建一个全新的对象
                // 2.并且执行[[Prototype]]链接
                // 4.通过`new`创建的每个对象将最终被`[[Prototype]]`链接到这个函数的`prototype`对象上。
                // self可能是ES6的箭头函数,没有prototype,所以就没必要再指向做prototype操作。
                if(self.prototype){
                    function Empty(){}
                    Empty.prototype = self.prototype
                    bound.prototype = new Empty()
                }
    
                // 这里实现的时上文描述的第三步
                // 3.生成的新对象会绑定到函数调用的this
                let result = self.apply(this, finalArgs);
    
                // 这里是实现上文描述的 new 的第 5 步
                // 5.如果函数没有返回对象类型`Object`(包含`Functoin`, `Array`, `Date`, `RegExg`,               //   `Error`),
                // 那么`new`表达式中的函数调用会自动返回这个新的对象。
                let isObject = typeof result === 'object' && result !== null
                let isFunction = typeof result === 'function'
    
                if(isObject || isFunction)
                    return result
    
                return this
            }else{
                // apply修改this指向,把两个函数的参数合并传给self函数,并执行self函数,返回执行结果
                return self.apply(thisArg, finalArgs)
            }
        }
        return bound
    }
    
    // Test
    let obj = {name : 'yato'}
    
    function original(a, b){
        console.log('this : ', this)
        console.log('typeof this : ', typeof this)
        this.name = b
        console.log('name: ', this.name)
        console.log('this: ', this)
        console.log([a,b])
    }
    
    let bound = original.bindFn(obj, 1)
    let newBoundInvoke = new bound(2)
    console.log('newBoundInvoke: ', newBoundInvoke)
    // this :  bound {}
    // typeof this :  object
    // name:  2
    // this:  bound { name: 2 }
    // [ 1, 2 ]
    // newBoundInvoke:  bound { name: 2 }
    

    总结

      1. bindFunction原型链中Function.prototype的一个属性,它是一个函数,修改this指向,合并参数传递给原函数,返回值是一个新的函数
      1. bind返回的函数可以通过new调用,这是提供的this参数被忽略,指向了new生成的全新对象。bind()内部模拟实现了new操作符
  • 相关阅读:
    类继承
    抽象基类 纯虚函数
    虚函数
    Java网络通信
    Java补补补
    刷LeetCode吧
    贝叶斯网络的
    vscode添加vue模板
    vue--项目实例
    Java01
  • 原文地址:https://www.cnblogs.com/qiqiloved/p/13475642.html
Copyright © 2020-2023  润新知