美文网首页
JavaScript之call、apply、bind模拟实现

JavaScript之call、apply、bind模拟实现

作者: smaVivian | 来源:发表于2019-02-25 12:00 被阅读0次

    call模拟实现

    var foo = {
        value: 1
    };
    function bar(name, age) {
        console.log(this.value);
        return {
            value: this.value,
            name: name,
            age: age
        }
    }
    bar.call(foo, 'Tom', 20);
    // Tom
    // 20
    // 1
    

    分析:

    1. call改变了this的指向,指向了foo
    2. bar函数执行

    步骤:

    1. 函数设置为对象属性
    2. 执行函数
    3. 删除函数

    模拟实现:

    Function.prototype.call2 = function(context) {
        var context = context || window;
        context.fn = this;
        // var args = Array.prototype.slice.call(arguments, 1);
        // for(var i = 1, len = arguments.length; i < len; i++) {
            // args.push('arguments[' + i + ']')
        // }
        // var result = eval('context.fn(' + args + ')');
        var args = [...arguments].slice(1);
        var result = context.fn(...args);
        delete context.fn;
        return result;
    }
    
    // 测试
    var value = 2;
    var foo = {
        value: 1
    };
    function bar(name, age) {
        console.log(this.value);
        return {
            value: this.value,
            name: name,
            age: age
        }
    }
    bar.call2(null); // 2
    
    console.log(bar.call2(foo, 'Tom', 20));
    // 1
    // Object {
    //    value: 1,
    //    name: 'Tom',
    //    age: 20
    // }
    

    apply模拟实现

    与call区别只是传参不同

    Function.prototype.apply2 = function(context) {
        var context = context || window;
        context.fn = this;
        var result;
        if(!arguments[1]) {
            result = context.fn();
        } else {
            result = context.fn(...arguments[1])
        }
        delete context.fn;
        return result;
    }
    
    // 测试
    var value = 2;
    var foo = {
        value: 1
    };
    function bar(name, age) {
        console.log(this.value);
        return {
            value: this.value,
            name: name,
            age: age
        }
    }
    
    //bar.apply2(null); // 2
    
    console.log(bar.apply2(foo, ['Tom', 20]));
    // 1
    // Object {
    //    value: 1,
    //    name: 'Tom',
    //    age: 20
    // }
    

    bind模拟实现

    分析:

    1. 返回一个函数
    2. 可以传入参数

    模拟实现版本1:

    Function.prototype.bind2 = function (context) {
        var self = this;
        var args = Array.prototype.slice.call(arguments, 1);
        return function () {
            var bindArgs = Array.prototype.slice.call(arguments);
            return self.apply(context, args.concat(bindArgs));
        }
    }
    
    // 测试
    var foo = {
        value: 1
    };
    
    function bar(name, age) {
        console.log(this.value);
        console.log(name);
        console.log(age);
    }
    
    var bindFoo = bar.bind2(foo);
    console.log(bindFoo()); // 1
    
    var bindFoo2 = bar.bind2(foo, 'Tom');
    bindFoo2(20);
    // 1
    // Tom
    // 20
    

    此处忽略了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.bind2(foo, 'daisy');
    
    var obj = new bindFoo('18');
    // undefined
    // daisy
    // 18
    console.log(obj.habit);
    console.log(obj.friend);
    // shopping
    // kevin
    

    分析:使实例obj可以根据原型链找到构造函数bar的值
    1.obj.proto = bindFoo.prototype
    2.bindFoo.prototype.proto = bar.prototype

    模拟实现版本2:

    Function.prototype.bind2 = function (context) {
        if (typeof this !== "function") {
          throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
        }
        var self = this;
        var args = Array.prototype.slice.call(arguments, 1);
        var fNOP = function () {};
        var fBound = function () {
            // 当作为构造函数时,this 指向实例,此时结果为 true,将绑定函数的 this 指向该实例,可以让实例获得来自绑定函数的值
            var bindArgs = Array.prototype.slice.call(arguments);
            return self.apply( this instanceof fNOP ? this : context, args.concat(bindArgs));
        }
        // 修改返回函数的 prototype,使:fBound.prototype.__proto__ = fNOP.prototype = this.prototype 实例就可以继承绑定函数的原型链中的值
        fNOP.prototype = this.prototype;
        fBound.prototype = new fNOP();
        return fBound;
    }
    

    相关文章

      网友评论

          本文标题:JavaScript之call、apply、bind模拟实现

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