美文网首页
bind的模拟实现

bind的模拟实现

作者: 怪物猎人 | 来源:发表于2018-03-19 22:56 被阅读0次

    原文出处

    JavaScript深入之bind的模拟实现

    bind

    我们在模拟 bind之前,先看看 bind实现了哪些功能。

            var foo = {
                value: 1
            };
    
            function bar() {
                console.log(this.value);
            }
    
            // 返回了一个函数
            var bindFoo = bar.bind(foo);
    
            bindFoo(); // 1
    

    由此我们可以首先得出 bind 函数的两个特点:

    1. 返回一个函数
    2. 改变了this的指向

    模拟实现第一步


              var foo = {
                value: 1
            };
    
            function bar() {
                console.log(this.value);
            }
            // 第一版
            Function.prototype.bind2 = function (context) {
                var self = this;
                return function () {  // 返回一个函数
                return self.apply(context);  // 改变绑定函数的this指向
                }
    
            }
    
            // 返回了一个函数
            var bindFoo = bar.bind2(foo);
            bindFoo();  // 1
    

    模拟实现第二步


    举个例子:

            var foo = {
                value: 1
            };
    
            function bar(name, age) {
                console.log(this.value);
                console.log(name);
                console.log(age);
    
            }
    
            var bindFoo = bar.bind(foo, 'daisy');
            bindFoo('18');
            // 1
            // daisy
            // 18
    

    由此我们可以看出:

    1. bind 绑定的时候可以传参数
    2. 在执行返回函数的时候也可以传参数

    我们可以用 arguments 模拟,如下:

            var foo = {
                value: 1
            };
    
            function bar(name, age) {
                console.log(this.value);
                console.log(name);
                console.log(age);
    
            }
            // 第二版
            Function.prototype.bind2 = function (context) {
    
                var self = this;
                // 获取bind2函数从第二个参数到最后一个参数
                var args = Array.prototype.slice.call(arguments, 1);
    
                return function () {
                    // 这个时候的arguments是指bind返回的函数传入的参数
                    var bindArgs = Array.prototype.slice.call(arguments);
                    return self.apply(context, args.concat(bindArgs));
                }
    
            }
    
            // 返回了一个函数
            var bindFoo = bar.bind2(foo, 'daisy');
            bindFoo('18');
            // 1
            // daisy
            // 18
    

    模拟实现第三步


    举个例子
    bind 还有一个特点,就是

    一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。

    也就是说当 bind 返回的函数作为构造函数的时候,bind 时指定的 this 值会失效,但传入的参数依然生效

            var value = 2;
    
            var foo = {
                value: 1
            };
    
            function bar(name, age) {
                this.habit = 'shopping';
                console.log(this.value);
                console.log(name);
                console.log(age);
            }
    
            bar.prototype.friend = 'kevin';
    
            var bindFoo = bar.bind(foo, 'daisy');
    
            var obj = new bindFoo('18');
            console.log(obj instanceof bar);
            
            // undefined
            // daisy
            // 18
            // true
            console.log(obj.habit);
            console.log(obj.friend);
            // shopping
            // kevin
    

    注意:尽管在全局和 foo 中都声明了 value 值,最后依然返回了 undefind,说明绑定的 this 失效了,这时候 this 已经指向 bar 实例了。

    所以我们可以通过修改返回的函数的原型来实现,让我们写一下:

            // 第三版
            Function.prototype.bind2 = function (context) {
                var self = this;  
                var args = Array.prototype.slice.call(arguments, 1);
    
                var fBound = function () {
                    var bindArgs = Array.prototype.slice.call(arguments);
                    // 当作为构造函数时,this 指向实例,此时结果为 true,将绑定函数的 this 指向该实例,可以让实例获得来自绑定函数的值
                    // 以上面的是 demo 为例,如果改成 `this instanceof fBound ? null : context`,实例只是一个空对象,将 null 改成 this ,实例会具有 habit 属性
                    // 当作为普通函数时,this 指向 window,此时结果为 false,将绑定函数的 this 指向 context
                    return self.apply(this instanceof fBound ? this : context, args.concat(bindArgs));
                }
                // 修改返回函数的 prototype 为绑定函数的 prototype,实例就可以继承绑定函数的原型中的值
                fBound.prototype = this.prototype;
                return fBound;
            }
    

    注意:bind2 中的 this 与 fBound 中的 this 不一样, 前者是绑定函数对象,后者如果返回函数作为构造函数调用,那么 this 指向返回函数实例对象, 如果当作普通函数调用的话,this 指向 window。

    模拟实现第四步

    但是在这个写法中,我们直接将 fBound.prototype = this.prototype,我们直接修改 fBound.prototype 的时候,也会直接修改绑定函数的 prototype。
    举例:

            Function.prototype.bind2 = function (context) {
                var self = this;
                var args = Array.prototype.slice.call(arguments, 1);
    
                var fBound = function () {
                    var bindArgs = Array.prototype.slice.call(arguments);
                    self.apply(this instanceof fBound ? this : context, args.concat(bindArgs));
                }
                fBound.prototype = this.prototype;
                return fBound;
            }
    
    
            function bar() {}
    
            var bindFoo = bar.bind2(null);
    
            bindFoo.prototype.value = 1;
    
            console.log(bar.prototype.value) // 1
    

    这个时候,我们可以通过一个空函数来进行中转:

                // 第四版
            Function.prototype.bind2 = function (context) {
    
                var self = this;
                var args = Array.prototype.slice.call(arguments, 1);
    
                var fNOP = function () {};
    
                var fBound = function () {
                    var bindArgs = Array.prototype.slice.call(arguments);
                    return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
                }
    
                fNOP.prototype = this.prototype;
                fBound.prototype = new fNOP(); // 原型链继承
                return fBound;
            }
    
    
            function bar() {}
    
            bar.prototype.friend = 'kevin';
            bar.prototype.value = 2;
    
            var bindFoo = bar.bind2(null);
    
            bindFoo.prototype.value = 1;
    
            console.log(bar.prototype.value) // 2
            console.log(bindFoo.prototype.value);  // 1
            
            var obj = new bindFoo();
            console.log(obj.friend);  // kevin
    

    相关文章

      网友评论

          本文标题:bind的模拟实现

          本文链接:https://www.haomeiwen.com/subject/qnguqftx.html