call的原理解析

140 阅读2分钟

call的用法是简单粗暴,但是要彻底理解深刻也需要一丢丢的研究. 一般简单的正常的使用就很容易啦 比如

fn1.call(obj)

就是执行fn1的时候,把fn1里面的this指向obj
其实可以这么理解 虽然有点邪门,但是确实可以这样去想

 fn1.call(obj) 相当于 obj.fn1= fn1  //obj有一个属性是fn1
 然后 执行 obj.fn1( )  这样fn1的this自然指向了obj, 然后用完之后过河拆桥,
 delete obj.fn1  把obj的fn1属性删掉,因为只是借用这个方法,obj本身并没有保留这个方法 ,所以删掉 

从这个角度去理解call做了什么就很容易了. 不过我在网上看见了一个邪门的东西

function fn1 (){ console.log( '1' )}
function fn2 (){ console.log( '2' )}
fn1.call(fn2) //1
fn1.call.call(fn2) //???? 

fn1.call(fn2)挺好理解的 因为fn2里面并没有this的操作 所以就只是fn1执行就完事了
那么fn1.call.call是什么呢 去浏览器实验 发现是得2

其实是相当于

let fn1Call =fn1.call 
 fn1Call.call(fn2) 

这样去理解就清晰一点了 就是执行fn1Call这个对象(函数)的call属性,并把fn2当参数传入

但是(fn1.call)这个方法是什么呢?让我们来造一个call函数出来

Function.prototype.myCall=function( ){
    let args=[]
    Object.assign(args,arguments)//复制一份参数数组出来
    args.splice(0,1)       //第一个是context 去掉
    let context =arguments[0] || window 
    context.func =this 
    context.func(...args)
    delete context.func
}
function fn1 (){ console.log( '1' )}
function fn2 (){ console.log( '2' )}
fn1.call(fn2) //1
fn1.call.call(fn2) //2

这个就是call的基本实现原理 那么为什么fn1.call.call(fn2)是2呢, 一步一步解析 fn1.call其实是等于这个函数

//fn1.call 就下面这个函数
function( ){
    let args=[]
    Object.assign(args,arguments)//复制一份参数数组出来
    args.splice(0,1)       //第一个是context 去掉
    let context =arguments[0] || window 
    context.func =this 
    context.func(...args)
    delete context.func
}

let fn1Pro =fn1.call

然后再慢慢解析 fn1Pro.call(fn2) 把fn1Pro的this改成 fn2 然后执行 fn1Pro 然后fn1Pro里面做了什么

let context =arguments[0] || window 
context.func =this 
context.func(...args)

fn1Pro就是把传进去的参数执行了 仅此而已,那么传进去了什么参数了 f2 那么执行的结果 就是 2
这样我们就能明白到底这call是怎么回事了