【SH手写系列】实现一个call apply bind

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

1. call( )
语法app

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

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

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

实现call开始prototype

// 首先没有给一个传入参数形式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,第二个参数是一个数组。code

两种方法意思是同样的,只是传参方式不一样。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( ) ip

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);
    };
  };
相关文章
相关标签/搜索