一些常用函数的polyfill

从今天开始,陆续写一些javaScript当中常用函数的polyfill(旧浏览器的兼容)。虽然说从实际工程的角度上讲目前已经有成熟的库在做这件事了,但是对于理解一些函数来讲还是很有帮助的。废话不多说,动手写起来。

Function.prototype.bind

首先Function.prototype.bind这个函数,最常用来改变调用其的函数的this的。最简单的用法大概是这个样子:

1
let testFunc = testObj.testFunc.bind(window)

这样就将testFunc执行时的this指向window。或者这样:

1
2
let write = document.write.bind(document);
let log = document.write.bind(console)

这样做的用意是使新生成函数在使用时其内部的this能够有正确的指向。
以上是最基础的用法,更多用法的可以参考MDN文档。总结来说bind函数要实现以下几个功能:

  • 创建了一个新函数,并绑定函数调用时的this至传入的对象
  • 柯里化:即支持在使用bind时提前传入新函数的部分参数
  • 使新生成的函数可以作为构造函数,即新函数的实例可以访问到被绑定函数的prototype。值得注意的时,此时在绑定时被传入的对象(或者说传入的this)应该被忽略,因为内部的this应该指向新函数声明地实例。
    下面我们就动手实现一个bind函数。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    if(!Function.prototype.bind){
    Function.prototype.bind = function(othis){
    if(typeof this !== 'function'){
    throw new Error('bind函数的调用者必须是一个function')
    }
    var self = this;//保存bind函数的调用者
    var args = Array.prototype.slice(arguments,1);//取得绑定时的除了被绑定对象之外的其余参数,这些参数要作为新生成函数的头一部分参数。
    function (){};//Temp作为一个过渡的临时函数。
    Temp.prototype = this.prototype;
    function bindedFunc(){
    var newArgs = Array.prototype.slice.call(arguments);
    //下面是最核心的部分,首先通过新函数调用时的this判断使用场景。
    //如果this instanceof Temp为true,说明此时新函数是用来做构造函数的(这是因为:instanceof的本质是实例沿着__proto__向原型链寻找, 如果可以找到某个函数的prototype则instanceof运算为true ,在本例子中寻找的路径是这样的:实例.__proto__ ==> bindFunc.prototype,bindFunc.prototype.__proto__ ==> Temp.prototype)。此时会忽略我们外层传入的othis而把新函数的调用时的this绑定到实例上。
    //如果this instanceof Temp 为false,说明就是普通的函数调用 ,将实例绑定到我们外层传入的对象(othis)。
    //最后,融合绑定时的参数与调用时的参数,作为新函数的参数
    return self.apply(this instanceof Temp?this:othis,args.concat(newArgs))
    };
    //通过原型链继承,在新函数作为构造函数使用时,使其实例也可以访问到被绑定函数的原型对象,
    bindedFunc.prototype = new Temp()
    return bindFunc
    }
    }