首先我要先搞懂这三个方法怎么用

1. call( )
语法

fun.call(thisArg, arg1, arg2, ...)

第一个参数理解为要在哪里执行fun(运行时指定的this值,他不一定是该函数执行时真正的this值,如果这个函数处于非严格模式下,则指定为null和undefined的this值会自动指向全局对象),后面的一串参数意思是执行fun时的传参

返回值是你调用的方法(fun)的返回值,若该方法没有返回值,则返回undefined

实现call开始

// 首先没有给一个传入参数形式context
Function.prototype._call(context){
    // 就要判断实际调用有没有这个参数
    let context = context ? context : window
    // 此时这个context就替代了上面的target
    context.func = this
    let result = context.func(args)   
    // 拿参数这步省略了,无错,按照我自己的写法这样也可以
    /*
    let [target, ...args] = [...arguments]
    判断了传入的目标对象是null等情况,再赋值window实际也能成功
    if(target == null || undefined){
        target = window;
    }
    */
    return result
    // 别忘记删除方法,这句写在return前面
    delete context.func
}

// 最终我自己不看答案写出的结果如下,实测可以 不知道有没有潜在问题

Function.prototype._call = function(){
    // let cont = context ? context : window
    let [target, ...args] = [...arguments]

    if(target == null || undefined){
        target = window;
    }
    target.fn = this
    let result = target.fn(...args)

    delete target.fn
    return result
}

2.apply( )
语法

fun.apply(obj,args)

第一个参数仍然理解为要在哪里执行fun,第二个参数是一个数组。

两种方法意思是一样的,只是传参方式不同。call后面可以给很多,而apply就只能给一个数组,在实际情况中按需使用吧。(我今天看懂继承再看看能多说点什么,还有试一下apply的返回是不是一样)

实现apply开始

Function.prototype._apply = function(){
    // 一切和call都差不多
   let [target, ...args] = [...arguments]
   
   // 考虑不传参 作用域的情况
   if(target == null || undefined){
        target = window
   }
   target.fn = this
   let result
   // 考虑参数存在情况。
   if(args){
    // 这里踩坑了,实际调用过程还是会把数组转化成非数组的 要加上...
        result = target.fn(...args)
   }else{
        result = target.fn()
   }
   
   delete target.fn
   return result
   
   // 有了call做铺垫基本还行,主要是判断args是否存在,还有参数是 数组的问题
}

3.bind( )

bind 是返回新的函数,以便稍后调用;apply 、call 则是立即调用原函数。也就是说 要定义一个新的变量 把bind返回的方法赋予给这个变量再去执行

语法

fun.bind(thisArg[, arg1[, arg2[, ...]]])

第一个参数我已经可以理解了,这里有一个新特性,就是如果bind返回的函数以new的形式去调用,第一个参数会被忽略,this仍然指向被调用的函数(fun)

arg1, arg2, … (可选)当绑定函数被调用时,要将这些参数(如果有的话)作为bind()的参数写在this后面。当绑定函数被调用时,这些参数会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们后面。
也就是说bind可以实现预设一些函数参数的功能(有啥用)

以下有两个例子来表达上面两点,搜来的:

function original(x){
  this.a=1;
  this.b =function(){return this.a + x}
}
var obj={
  a:10
}
var  newObj = original.bind(obj,2) //传入了一个实参2
var newObjChild = new newObj()
console.log(newObj.a)  //输出 1, 说明返回的函数用作构造函数时obj(this的值)被忽略了

console.log(newObj.b()) //输出3 ,说明传入的实参2传入了原函数original

var sum = function(x,y) { return x + y }; 

var succ = sum.bind(null, 1); //让this指向null,其后的实参也会作为实参传入被绑定的函数sum

succ(2) // => 3:  可以看到1绑定到了sum函数中的x

另外的,bind和函数柯里化好像有点关系,一会儿回家讨论下

实现bind开始
bind好难我自己写不出来,只好一遍一遍看了

Function.prototype._bind = function(context){
    if(typeof this !== 'function'){
        throw new TypeError('被绑定的对象需要是函数')
    }
    var self = this   // 保存起原指针(作用域)

    var args = [].slice.call(arguments, 1)  
    console.log(arguments)  
    // =====> bind被调用时的传入参数

    fBound = function(){ //  this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用

        console.log(arguments)  
        // =====> bind返回的函数fBound被调用时传入的参数

        return self.apply(this instanceof fBound ? this : context, args.concat([].slice.call(arguments)))
        /* 如果被Fbound被用作了构造函数,
        构造出的实例一定是Fbound类型,
        如果没有,那么当前的this和Fbound没有任何关系。
        参数为bind被调用时和Fbound被调用时的参数联合 */
    }

    // -----------------------------------------------
    
    var func = function(){}    // 这个到底是干嘛的??
    //维护原型关系
    console.log(this.prototype)   // 一个constructor指向原函数
    console.log(func.prototype)    // 什么都没有
    if(this.prototype){
        func.prototype = this.prototype
    }
    //使fBound.prototype是func的实例,返回的fBound若作为new的构造函数,新对象的__proto__就是func的实例
    fBound.prototype = new func()
    
    // ------------------------------框住的我先不看了 不能理解

    return fBound
}

留一个最新的MDN的polyfill

  Function.prototype.bind = function() {
    var thatFunc = this, thatArg = arguments[0];
    var args = slice.call(arguments, 1);
    if (typeof thatFunc !== 'function') {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      throw new TypeError('Function.prototype.bind - ' +
             'what is trying to be bound is not callable');
    }
    return function(){
      var funcArgs = args.concat(slice.call(arguments))
      return thatFunc.apply(thatArg, funcArgs);
    };
  };

绿绿
33 声望3 粉丝