zoukankan      html  css  js  c++  java
  • 【js基础修炼之路】- 手把手教你实现bind

    手写bind前我们先回顾一下bind有哪些特性,以便更好的理解bind和实现bind。

    bind的特性

    var obj = {
        a: 100,
        say(one, two) {
            console.log(this.a, one, two);
        }
    }
    var obj2 = {
        a: 300
    }
    var res = obj.say.bind(obj2, 1, 2);
    res();
    //300 1 2
    

    可以看出:

    • bind是函数的方法,只有函数可以调用

    • bind的第一个参数是this指向,剩下的参数作为调用者的参数

    • bind方法返回的是一个函数,需要再次调用才能执行

    function test(){
    	this.a = 10,
    	this.b = 20
    };
    var foo = {
    	a:200
    }
    var res = test.bind(foo);
    var res2 = new res();
    console.log(res2);
    //test {a: 10, b: 20}
    

    从上面可以看出,new之后this执行不跟随bind的第一个参数了,知道new是怎么实现的小伙伴一定知道为什么(不知道的可以看一下这篇文章的末尾 https://juejin.im/post/5c492603e51d451d200e4ebb ) 此时的this指向了res2。
    知道了bind的特性,下面我们来实现一下bind

    手把手教你实现bind

    我们知道bind返回的是一个函数,调用者也是一个函数,并且bind改变了this指向,而且bind还可以传参,下面我们来实现一下这些功能:

     Function.prototype.bind = function(oThis) {
     	// 判断调用者是不是函数
     	if(typeof this != 'function'){
     		  throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
         }
         //保存this(this指向调用bind者)
         var fToBind = this;
         //获取传入bind函数的第二个及其后面的参数(除去this参数)
         var aArgs = Array.prototype.slice.call(arguments,1);
         var fBound = function(){
         	//this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用
         	//== false的时候说明当做了普通函数来调用,this为bind的第一个参数
         	return  fToBind.apply(this instanceof fBound ? this : oThis,aArgs.concat(Array.prototype.slice.call(arguments)));
         }
         // 返回新函数
         return fBound;
     }
    

    上面的代码实现了一个基本的bind,但是还有一些问题,例如上面只是绑定了this,但是原函数的原型新函数并没有继承,所以我们需要再次继承一下原型:

     Function.prototype.bind = function(oThis) {
     	// 判断调用者是不是函数
     	if(typeof this != 'function'){
     		  throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
         }
         //保存this(this指向调用bind者)
         var fToBind = this;
         //获取传入bind函数的第二个及其后面的参数(除去this参数)
         var aArgs = Array.prototype.slice.call(arguments,1);
         var fBound = function(){
         	//this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用
         	//== false的时候说明当做了普通函数来调用,this为bind的第一个参数
         	return  fToBind.apply(this instanceof fBound ? this : oThis,aArgs.concat(Array.prototype.slice.call(arguments)));
         }
          //绑定原型
          fBound.prototype = this.prototype;
         // 返回新函数
         return fBound;
     }
    

    本以为大功告成,但是还有一个问题,看下面的例子:

     function bar() {}
     var bindFoo = bar.bind(null);
     bindFoo.prototype.value = 1;
     console.log(bar.prototype.value) // 1
    

    我只改变了bindFoo的原型,bar的为什么也跟着变了,因为在写bind的时候把bar的原型赋给了bindFoo,所以导致了这种情况,下面我们用一个中转的函数来解决这个问题:

     Function.prototype.bind = function(oThis) {
     	// 判断调用者是不是函数
     	if(typeof this != 'function'){
     		  throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
         }
         //保存this(this指向调用bind者)
         var fToBind = this;
         //获取传入bind函数的第二个及其后面的参数(除去this参数)
         var aArgs = Array.prototype.slice.call(arguments,1);
         var fNOP = function() {};
         var fBound = function(){
         	//this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用
         	//== false的时候说明当做了普通函数来调用,this为bind的第一个参数
         	return  fToBind.apply(this instanceof fBound ? this : oThis,aArgs.concat(Array.prototype.slice.call(arguments)));
         }
      	 // 为了让 fBound 构造的实例能够继承绑定函数的原型中的值
         if (this.prototype) {
             fNOP.prototype = this.prototype;
         }
         // 下行的代码使fBound.prototype是fNOP的实例,因此
         // 返回的fBound若作为new的构造函数,new生成的新对象作为this传入fBound,新对象的__proto__就是fNOP的实例
         fBound.prototype = new fNOP();
         // 返回新函数
         return fBound;
     }
    

    对于代码有疑问的小伙伴可以留言或者看注释!!!

    记住,在你成功之前,每前进一步遇到的都将是更大的困难,所以微笑面对每一个困难 -----至有理想的自己
  • 相关阅读:
    【js】数据表格开启同比化显示,增加对比性
    Kotlin 使用协程编写高效的并发程序
    Kotlin泛型的高级特性
    kotlin基础
    Java接口和抽象类区别
    Java内存泄漏
    jmeter,注意:您可以通过定义属性resultcollector.action_if_file_exists来避免这个弹出框
    性能测试术语
    jenkins正常显示jmeter的html报告设置
    jmeter: beanshell后置处理程序,清空文件和保存json提取器提取的数据到文件中
  • 原文地址:https://www.cnblogs.com/-yu-ze-/p/10330526.html
Copyright © 2011-2022 走看看