Call的内部原理

fn1基于proto找到Funtion.prototype.call方法,把call方法执行,在call方法内部执行的时候,帮助我们把fn1执行了,并且让fn1中的this变为obj,并且把10/20传递给fn1

  1. //Cal内部实现的方法
  2. Function.prototype.call=function call(context,...params){
  3. //this->fn1
  4. //context->obj
  5. //params-[10,20]
  6. // 具体做的事情,把this(fn1)执行,让它里面的this指向context(obj),并且把params([10,20]) 传递给函数
  7. }
  8. const fn1=function fn(){
  9. console.log(1);
  10. };
  11. let obj={
  12. }
  13. fn1.call(obj,10,20);

任何方法都是如此

  • 例如arr.push(100)
    • arr首先基于proto找到Array.prototype.push方法
    • 把找到的push方法执行
    • 在push方法的内部,

Array.prototype.push=function(val){
this -> arr
val -> 100
把val插入到this的末尾
}
image.png

假设没有call方法,我们想让fn执行怎么办

// 假设没有call方法,我们想让fn执行,方法中的this是obj,再传递10/20
//   + 首先给obj设置一个属性,属性值是当前要执行的函数fn
//   + 直接基于成员访问的方式,就可以把函数执行,并且this就是obj
const fn = function fn(x, y) {
    console.log(this, x + y);
};
let obj = {
    name: 'obj'
};
obj.fn=fn;
obj.fn();
fn.call(obj,10,20)=>在下面👇

重写内置Call

image.png

Function.prototype.call = function call(context, ...params) {
    // this->fn 要执行的函数   context->obj 要改变的THIS   params->[10,20] 要传递的实参
      // 必须保证context是个对象 原始值类型不能设置成员 执行的时候会报错
    if (context == null) context = window;//如果第一个参数不传或者NUll this默认更改为window
    let type = typeof context; //判断所属对象
    if (type !== "object" && type !== "function") {
        // 把传递的原始值类型变为对应的对象类型值
        context = Object(context);
    }
    let key = Symbol(),
        result;
    context[key] = this; //给context添加属性 但是不能随便加 万一context本身的属性名就是你写的 这样会被覆盖 所以用了唯一值
    result = context[key](...params);
    delete context[key]; // 用完记得把新加的属性删除掉
    return result;
};

call中this是谁,最终就把谁执行

技巧:

  • 一个call,让call前面的执行
  • 两个及以上call,让call后面的执行

    练习

const fn1 = function fn1() {
    console.log(1);
};
const fn2 = function fn2() {
    console.log(2);
};
fn1.call(fn2); //把fn1执行了 
//=>找到call方法 此时call中的this是fn1, call方法中的this是谁 就是把谁执行
fn1.call.call.call(fn2); 
//“CALL方法”.call(fn2)  ->把“CALL方法”执行,让方法中的this是fn2 -> 相当于fn2执行了  
Function.prototype.call(fn2); //把Function.prototype执行,没有任何输出
Function.prototype.call.call.call(fn2); //“CALL方法”.call(fn2) -> 让“CALL方法”执行,让它中的this是fn2 -> 相当于fn2执行  2

无标题.png