关于call,apply,bind的原生js实现

107 阅读2分钟

我们知道在JavaScript里面有关于this的指向的问题,原生JavaScript里面也有四种绑定方式,

(1)默认绑定

(2)隐式绑定

(3)显示绑定(通过call,apply,bind等实现)

(4)new关键字绑定

下面我们主要对显示绑定来进行实现以及说明

一.call方法的实现

我们知道call可以改变this的指向

function foo(a){
  console.log('foo函数',a)
}
foo.call('zb',10)

原本在执行的时候,foo默认被全局对象执行,在浏览器里面是window对象,在node里面是global对象,现在我们通过call方法将this指向改变为zb这个对象。

image.png

所以我们想通过原生js实现这个call方法具体实现如下,我们将我们模拟的这个函数挂载到Function这个对象上,方便我们调用。

Function.prototype.zbCall = function(){
//这里的this指向是被执行的函数
    let fn = this
    fn()
}
function foo(a){
  console.log('foo函数',a)
}
foo.zbCall()

可以看到foo函数被执行了 image.png

我们接着进行实现,现在我们需要考虑zbCall函数的第一个参数,第一个参数是this的指向,第二个参数是被执行函数的参数所以我们加入形参thisArg和args

Function.prototype.zbCall = function(thisArg,...args){
//这里的this指向是被执行的函数
    let fn = this
    //如果zbCall函数的第一个参数为空或者未定义,我们需要进行处理,将它默认指向全局对象
    thisArg = (thisArg !== undefined && thisArg !== null) ?Object(thisArg) :window
    thisArg.fn = fn
    //执行函数,
    const result = thisArg.fn(...args)
    delete thisArg.fn
    //将执行结果返回
    return result
}
function foo(a){
  console.log('foo函数',a,this)
}
foo.zbCall(null,10)

image.png

说明没有问题

二.apply的实现

apply的实现和call基本一样,唯一不同的是apply函数的第二个参数是一个数组,只需要将上面的代码稍微修改就行

Function.prototype.zbApply = function(thisArg,args){
//这里的this指向是被执行的函数
    let fn = this
    //如果zbCall函数的第一个参数为空或者未定义,我们需要进行处理,将它默认指向全局对象
    thisArg = (thisArg !== undefined && thisArg !== null) ?Object(thisArg) :window
    thisArg.fn = fn
    //执行函数,
    const result = thisArg.fn(args)
    delete thisArg.fn
    //将执行结果返回
    return result
}
function foo(a){
  console.log('foo函数',a,this)
}
foo.zbApply(null,10)

image.png

三.bind函数的实现

bind函数只是改变了this指向,在函数调用bind函数之后,会生成一个新的函数,

Function.prototype.zbind = function(thisArg,...args){
 let fn = this
 thisArg = (thisArg !== undefined && thisArg !== null) ?Object(thisArg) : window
 return function Fbind(...args2){
    thisArg.fn = fn
    let final = [...args,...args2]
    let result = thisArg.fn(...final)
    delete thisArg.fn
    return result
 }
}
function foo() {
    console.log('foo函数', this);
}

function sum(a, b, c, d) {
    console.log(a, b, c, d);
}
let newsum = sum.zbBind('zb', 10, 20)
let res = newsum(30, 40)   //10 20 30 40
//改变了this的指向,并且生成了新的函数
let s = foo.bind('1111')
s()  // foo函数 [String: '1111']