资讯专栏INFORMATION COLUMN

【build your own xxx】实现你自己的bind函数

dantezhao / 2141人阅读

今天来实现JavaScript的bind函数。
首先看MDN的bind函数描述:

从上面可以看出来,var A = B.bind(this)函数其实干了这几件事情:

返回一个函数,且这个函数后面运行时的this就是bind(this)传入的this

接收参数,这些参数(如果有的话)作为bind()的第二个参数跟在this(或其他对象)后面,之后它们会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们的后面

使用new操作bind函数返回的函数时,之前传入的this会被忽略,也就是说new的优先级高于bind

第一步

首先实现第一步:

Function.prototype.Zbind = function (othis) {
    var originFunc = this;
    return function () {
        originFunc.apply(othis);
    }
}


var obj = {

}

function createAgumon() {
    this.name = "agumon";
}
var createAgumonBind = createAgumon.Zbind(obj);
createAgumonBind();   
obj;// {name: "agumon"}
第二步

第二步考虑传参的问题,首先看看原生的bind函数是如何传参的:

var obj = {

}
function createAgumon(gender, age) {
    this.name = "agumon";
    this.gender = gender;
    this.age = age;
}
var createAgumonBind = createAgumon.bind(obj, "female");
createAgumonBind(22);

可以看出来在bind函数中能先传部分参数,运行bind返回的函数时可以再传入部分参数。
自己实现:

Function.prototype.Zbind = function (othis) {
    var originFunc = this;
    var partArgs = [].slice.call(arguments, 1);
    var func = function() {};
    var boundFunc = function () {
        var finalArgs = partArgs.concat([].slice.call(arguments));
        return originFunc.apply(othis, finalArgs);
    }

    return boundFunc;
}


var obj = {

}

function createAgumon(gender, age) {
    this.name = "agumon";
    this.gender = gender;
    this.age = age;
}

var createAgumonBind = createAgumon.Zbind(obj, "female");
createAgumonBind(22);
obj;// {name: "agumon", gender: "female", age: 22}
第三步

使用new来调用bind返回的函数时,会忽略bind传入的this
new操作和普通的函数调用有哪些区别?
粗略的来讲,例如new F()这样的调用,有以下几个步骤:

新建一个对象,var o = new Object()

设置原型链,o.__proto__ = F.prototype

把F函数体内的this绑定为o,并且执行F函数的代码

判断F的返回类型:
如果是值类型,则返回o
如果是引用类型,则返回该引用类型对象

开始实现:

Function.prototype.Zbind = function (othis) {
    var originFunc = this;
    var partArgs = [].slice.call(arguments, 1);
    var func = function() {};
    var boundFunc = function () {
        var finalArgs = partArgs.concat([].slice.call(arguments));
        return originFunc.apply(this instanceof boundFunc ? this : othis, finalArgs);
    }


    return boundFunc;
}


var obj = {}

function createAgumon(gender, age) {
        this.name = "agumon";
        this.gender = gender;
        this.age = age;
}

var createAgumonBind = createAgumon.Zbind(obj, "female");
var newObj = new createAgumonBind(22);
obj // {}
newObj // {name: "agumon", gender: "female", age: 22}

关键的地方在于这里:this instanceof boundFunc ? this : othis,如果是new操作的话,此时this的__proto__已经指向了boundFunc,所以使用instanceof可以检测出是否在使用new操作

小细节

原型丢失
刚刚实现的Zbind方法有个小问题:

Function.prototype.Zbind = function (othis) {
            var originFunc = this;
            var partArgs = [].slice.call(arguments, 1);
            var func = function() {};
            var boundFunc = function () {
                var finalArgs = partArgs.concat([].slice.call(arguments));
                return originFunc.apply(this instanceof boundFunc ? this : othis, finalArgs);
            }


            return boundFunc;
        }


        var obj = {

        }

        function createAgumon(gender, age) {
            this.name = "agumon";
            this.gender = gender;
            this.age = age;
        }
        createAgumon.prototype.college = "THU"
        var createAgumonBind = createAgumon.Zbind(obj, "female");
        var newObj = new createAgumonBind(22);
        console.log(newObj.college)// undefined

可以看出来原型链丢失了,newObj.college得是"THU"才行

修改:

Function.prototype.Zbind = function (othis) {
            var originFunc = this;
            var partArgs = [].slice.call(arguments, 1);
            var func = function() {};
            var boundFunc = function () {
                var finalArgs = partArgs.concat([].slice.call(arguments));
                return originFunc.apply(this instanceof boundFunc ? this : othis, finalArgs);
            }

            func.prototype = originFunc.prototype;
            boundFunc.prototype = new func();

            return boundFunc;
        }


        var obj = {

        }

        function createAgumon(gender, age) {
            this.name = "agumon";
            this.gender = gender;
            this.age = age;
        }
        createAgumon.prototype.college = "THU"
        var createAgumonBind = createAgumon.Zbind(obj, "female");
        var newObj = new createAgumonBind(22);
        console.log(newObj.college)// "THU"

为什么要使用func.prototype = originFunc.prototype;boundFunc.prototype = new func();,而不是直接用boundFunc.prototype = originFunc.prototype;是因为这样写的话,修改boundFunc.prototype会影响到原函数的prototype。

that"all

参考资料:
mdn-bind
javascript中,new操作符的工作原理是什么?

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/108034.html

相关文章

  • build your own xxx实现自己call和apply

    摘要:新开一个坑,起名为,自己造一些小轮子。之前貌似在知乎上看到一个问题是说如何使用实现它原生的和方法,今天我来实现一番。但是,这样等于说只给传了一个数组参数,并不能达到目的。同理来实现参考资料深入之和的模拟实现 showImg(https://segmentfault.com/img/bVbbHCv?w=1123&h=629); 新开一个坑,起名为【build your xxx】,自己造一...

    qpal 评论0 收藏0
  • Build your own AngularJS》笔记分享

    摘要:初次写文章,请多多包涵我最近正在根据这本书从头开始实现了一遍的框架。笔记目录链接个人认为本书对于想了解框架源码的读者来说相当有用,完全值得去购买这本书书本主页。因为是初学者,笔记里可能有一些错误,我也会继续修改。 (初次写文章,请多多包涵) 我最近正在根据《Build your own angularJS》这本书从头开始实现了一遍AngularJS的框架。我把相关的源码和我的个人学习笔...

    firim 评论0 收藏0
  • Spring Boot (一)helloworld

    摘要:第二个类级别注解是。将引导应用程序,启动,从而启动自动配置服务器。比如想使用不同版本的,具体如下在标签中还可以指定编译的版本和项目的编码格式指定项目编码为使用插件可以为项目提供的操作方式,的个,默认。 引言 Spring 框架对于很多 Java 开发人员来说都不陌生。Spring 框架包含几十个不同的子项目,涵盖应用开发的不同方面。如此多的子项目和组件,一方面方便了开发人员的使用,另外...

    go4it 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<