js 自定义 (call,bind)总结

897 阅读3分钟

一直以来在看这些手写自定义方法,其实目的不是看实现结果,而是看过程的思路,从中会学习到一些逻辑的思维,虽然都是一些简单的小方法,但是还是值得我们一看的。

手写call

第一个我们就来看看这个call,可能大家都见惯不惯了,但还是以call 为例总结一下,apply 方法思路相同。首先我们来看看自带call方法的用途吧...

var value = 'this is window';
var foo = {
    value : 'this is foo'
}
function bar(name,age){
    console.log(this.value);
}
bar.call(foo);  //this is foo

运行结果是'this is foo' , call函数在这里修改了bar运行时this的指向,相当于执行了foo.value,所以输出的是foo对象下的value。那我们自己封装的思路就是这样了:

Function.prototype.call2 = function(obj){
    var obj = obj || window;   //如果obj为null,那么我们让它指向window
    obj.fn = this;
    obj.fn();
    delete obj.fn;  //执行完后接的删除这个临时方法
}
var value = 'this is window';
var foo = {
    value : 'this is foo'
}
function bar(name,age){
    console.log(this.value);
}
bar();  //this is window
bar.call1(foo);  //this is foo

在Function的原型上新建方法call2,传递第一个参数,也就是需要指向的那个对象,然后this指向当前调用call2 方法的对象,然后执行。 call方法是可以接收参数的,所以我们继续来完善这个方法。

Function.prototype.call2 = function(obj){
    var obj = obj || window;
    var arr = [];   //临时创建一个空的数组,来存放传进来的参数
    obj.fn = this;
    for(var i = 1; i < arguments.length; i++){   //参数第一个是要指向的对象,所以我们从第二个参数开始循环添加,i=1
        arr.push('arguments['+i+']');
    }
    var result = eval('obj.fn('+arr.join()+')'); 
    delete obj.fn;
    return result;
}

arr 数组存后结果是 ["arguments[1]", "arguments[2]"...],arr.join()把它转成字符串,所以我们用到eval 方法,可以把string字符串里的内容运行,把结果赋值给result,返回出去。 完整的代码是这样的:

Function.prototype.call2 = function(obj){
    var obj = obj || window;
    var arr = [];
    obj.fn = this;
    for(var i = 1; i < arguments.length; i++){
        arr.push('arguments['+i+']');
    }
    var result = eval('obj.fn('+arr.join()+')');
        delete obj.fn;
        return result;
}
var value = 'this is window';
var foo = {
    value : 'this is foo'
}
function bar(name,age){
    console.log(this.value);
    console.log(name,age);
}
bar.call2(foo,'fly',30);   //this is foo   fly  30

手写bind方法

这个也和call,apply 方法类似,bind方法返回的是一个绑定this后的函数,并且该函数并没有执行,需要手动去调用,而call,apply是立即执行。有了上面的自定义方法call2 ,这里bing我就简短来说了,直接上代码。

Function.prototype.bind2 = function(obj){
    if(typeof this!== 'function'){
        return this;
    }
    var arg = Array.prototype.slice.call(arguments,1); //从第二个参数开始截取
    var that = this;  //这里指向当前调用者,函数
    //创建返回的函数
    function bindFn(){
        var _this = this instanceof bindFn ? this : obj;  // 返回的函数可能涉及到new 操作符调用,所以这里需要判断一下,当前的对象 是否是 bindFn对象的实例,否则返回传进来的对象obj
        return that.apply(_this,arg.concat(Array.prototype.slice.call(arguments)));
    }
    var fn = function(){};  // 创建空的函数,让这个函数的原型接收当前函数的属性和方法,然后吧实例赋值给bindFn的原型
    fn.prototype = this.prototype;
    bindFn.prototype = new fn();
    return bindFn;  //最后返回bindFn 函数
}