zoukankan      html  css  js  c++  java
  • 源码来袭:bind手写实现

    理解建议:如果对this指向规则不了解的话,建议先了解this指向规则,最好还能对call和apply的使用和内部原理也有所了解,不然直接研究bind还是会有些难度的。

     一、bind()的使用

    //html
    <button id="btn"></button>
    //js
    var list = {
        init:function(){
            this.ms = "duyi";
            this.dom = document.getElementById("btn");
            this.bindEvent();
        },
        bindEvent:function(){
            this.dom.onclick = this.showMessage.bind(this);
        },
        showMessage:function(){
            alert(this.ms);
        }
    }
    list.init();

    在单对象编程中,有一种非常典型的bind()的应用,就以上面的示例来说,当出现给DOM绑定事件回调函数时,又还需要继续保持函数的this指向原来的对象,就可以按照示例的这种方式来实现:this.dom.onclick = this.showMessage.bind(this);

    我们知道call和apply可以改变函数执行的this指向,但是call和apply都是立即执行该函数,而bind是将this指向绑定到指定的对象上,并且返回函数并维持this指向这个对象。接下来再来看看bind的参数设置示例:

    function show(x,y,z,w){
        console.log(this,x,y,z,w);
    }
    var DuyiO = {
        x : 20
    }
    var newShow = show.bind(DuyiO,"1","2",3);
    newShow(4);//Object {x: 20} "1" "2" 3 4

    bind的参数和call非常类似,唯一的区别就在于除了调用bind时传入参数外,还可以在正式执行时传入参数,两次传入参数以拼接的方式作为函数执行的实参。但是需要注意的是,第一个参数作为函数的this指向对象必须要在调用bind方法时传入,如果调用bind方法不传入任何参数,函数的this指向就会绑定到window上。比如下面这种情况:

    var newShow = show.bind();
    newShow(DuyiO,"1","2",3,4);//Window {…} Object {x: 20} "1" "2" 3

    最后还有一个基本上不会被应用到的功能,就是返回的函数被new关键字用来创建一个新的对象,而构造函数还是原函数本身(第二个示例中的show)。这个功能在模仿bind源码不能100%实现,但是也可以间接的实现其需要的功能。

     二、bind手写实现

    1.首先实现函数调用bind修改this指向即参数设置:

    1 Function.prototype.MyBind = function(target){
    2     var self = this;
    3     var args = [].slice.call(arguments,1);
    4     var f = function(){
    5         return self.apply( target || window,args );
    6     }
    7     return f;
    8 }

    2.接着再来实现函数正式调用执行时传入设置:

    1 Function.prototype.MyBind = function(target){
    2     var self = this;
    3     var args = [].slice.call(arguments,1);
    4     var f = function(){
    5         var _arg = [].slice.call(arguments,0);
    6         return self.apply( target || window,args.concat(_arg) );
    7     }
    8     return f;
    9 }

    3.最后实现当返回函数被new操作符引用作为构造函数依然指向原函数(模拟实现功能):

     1 Function.prototype.MyBind = function(target){
     2     var self = this;
     3     var args = [].slice.call(arguments,1);
     4     var temp = function(){};
     5     var f = function(){
     6         var _arg = [].slice.call(arguments,0);
     7         return self.apply(this instanceof temp ? this : ( target || window ),args.concat(_arg) );
     8     }
     9     temp.prototype = self.prototype;
    10     f.prototype = new temp();
    11     return f;
    12 }

    这个模拟实现主要有两个关键点需要重点理解:

    a.代码第七行中的this instanceof temp ? this : ( target || window ):当返回函数f被new作为构造函数引用时,这时候this指向了函数执行时内部隐式添加在变量对象上的this(这里不清楚的话可以参考JavaScript中的this指向规则),当然普通调用执行就是指向self。

    b.代码第九行和第十行为什么需要改变f的原型,这就是我前面讲的模拟实现方法构造,我们知道bind在JS内部实现的是其返回函数还是那个原来的函数,这里我们多加了一层f来实现的,所以在函数被当做构造函数的时候,将f的原型指向self也可以实现其功能,但是构造的实例对象是基于f实现的,最终构造原型链还是指向self原型,该有的方法属性依然都会有。只是在原型链上多了f这个包装层。

  • 相关阅读:
    微信小程序---app.json中设置背景色不生效解决办法
    给网站设置ICO图标
    ajax事件(五)
    ajax关于主流中的异类:应对Opera(四)
    dashboard
    tomcat 清理日志
    jQuery datatable
    php wampserver 80 端口无法开启的解决方法
    mysql 行列转换
    jQuery-2.1.4.min.js:4 Uncaught TypeError: Illegal invocation
  • 原文地址:https://www.cnblogs.com/ZheOneAndOnly/p/10423825.html
Copyright © 2011-2022 走看看