this(函数执行的主体)

  • 全局上下文的this是window
  • 块级上下文没有自己的this,所用的this是继承上下文的this(箭头函数也是)

    事件绑定

    事件绑定的this,指定绑定的元素

    普通函数执行

    其他情况看点,点前面是谁,this就是谁;没点就是window
    image.png

    call、apply、bind强制改变this指向问题

  • 都是用来改变某一个函数中this关键字指向的

image.png

初步了解 call&bind&apply

call改变函数执行的this,并且执行函数

语法: function.call(thisArg, arg1, arg2, …) 参数:第一个this指向 第二个及后面的是实参 返回值:函数执行的返回值

bind改变函数执行的this,但是不执行函数

语法:function.bind(thisArg[, arg1[, arg2[, …]]]) 参数:第一个this指向 第二个及后面的是实参 返回值:改变this指向的函数体

apply改变函数执行的this,并且执行函数

语法 :func.apply(thisArg, [argsArray]) 参数:第一个参数是this指向,第二个参数是数组 返回值:函数执行的返回值

  • apply与call的区别在于传参方式不同我们需要把所有参数放在这个数组里,apply会把数组里的每一项一个一个传给函数

示例:

  1. let a = 1;
  2. let obj = {
  3. a: 2,
  4. fn: fn
  5. };
  6. function fn(b, c) {
  7. console.log(this.a);
  8. return b + c;
  9. };
  10. fn(4, 5); //正常执行fn,this指向的就是window,此时a为window里的属性,因为没有,输出undefined
  11. fn.call(obj); //fn.call(obj)执行一次函数,会把this改成obj 不输出
  12. fn.bind(obj);
  13. console.log(fn.bind(obj));//bind改变后会输出函数体,但不执行,返回的是改变后的函数体,看不出来,但已经改变了
  14. fn.apply(obj, [1, 2]); //apply的传参方式不同,必须以一个数组的方式传参,只接收两个参数:第一个参数是this指向,第二个参数值是数组,把所有需要传递的参数都放在数组里;返回值是函数结果,也会执行词义函数
  15. console.log(fn.apply(obj, [1, 2]));

详细了解 call&bind&apply

call

1、[fn].call([this],[param]…)

  • fn.call:当前实例(函数fn)通过原型链的查找机制,找到function。prototype上的call方法
  • fn.call() : 把找到的call方法执行
  • 当call方法执行的时候,内部处理了一些事情
  • =>首先把要操作函数中的this关键字变为call方法第一个传递的实参值
  • =>把call方法第二个及第二个以后的实参获取到
  • =>把要操作的函数执行,并且把第二个以后的传递进来的实参传给函数

    1. let sum=function(a,b){
    2. console.log(this,a+b);//=>opt
    3. };
    4. let opt={n:20};
    5. sum.call(opt,20,30)//=>call执行 call中的this是sum 把this(call中的)中的“this关键字”改为opt 把this(call中的)执行,把20,30分别传递给它 //=>sum中this:opt a=20 b=30
    6. sum.call.call(opt)
    7. //1.sum.call 找到Function.prototype上的call方法(也是一个函数,也是函数类的一个实例,也可以继续调用call/apply等方法) =>A(函数)
    8. //2.A.call(opt) 继续找到原型上的call方法,把call方法执行:把A中的this关键字修改为opt,然后把A执行

    CALL中的细节

  • 1.非严格模式下,如果参数不传,或者第一个传递的是null/undefined,THIS都指向WINDOW

  • 2.在严格模式下(加 ‘use strict’ 字符串就是严格模式),第一个参数是谁,THIS就指向谁(包括null/undefined),不传THIS就是undefined

    fn.call(obj, 10, 20);//=>this:obj a=10 b=20
    fn.call(10, 20);//=>this:10 a=20 b=undefined
    fn.call();//=>this:window a=undefined b=undefined
    fn.call(null);//=>this:window
    fn.call(undefined);//=>this:window
    

    阿里call面试题

    /*
    Function.prototype.call = function (context, ...arg) {
      //=>my_call方法中的this就是我们要处理的那个函数(fn/sum...)
      this.toString().replace('this', context);
      this(...arg);
    };
    */
    function fn1() {
      console.log(1);
    }
    function fn2() {
      console.log(2);
    }
    fn1.call(fn2);//=>1
    /*
    * 执行CALL方法
    *   1.CALL方法中的THIS:FN1
    *   2.CALL方法中的CONTEXT:FN2
    */
    fn1.call.call(fn2);//=>2
    /*
    * 执行的是最后一个CALL方法
    *   1.THIS:FN1.CALL
    *   2.CONTEXT:FN2
    */
    //=>第一次执行最后面的CALL,代码执行第一步
    //FN1.CALL.toString().replace('this', FN2);
    // FN1.CALL=function (context, ...arg) {
    //     FN2.toString().replace('this', context);
    //     FN2(...arg);
    // };
    //=>第一次执行最后面的CALL,代码执行第二步
    //FN1.CALL();
    /*
    *  第二次执行CALL方法
    *    CONTEXT='UNDEFINED'
    *    FN2.toString().replace('this', undefined);
    *    FN2();
    */
    //===============另一种思路
    fn1.call(fn2);
    /*
    * CALL中的THIS是FN1,把FN1中的THIS关键字修改为FN2,然后再把FN1执行  =>"CALL方法中的THIS是谁,最后执行的就是谁"
    */
    fn1.call.call(fn2);
    /*
    * 第一次执行最末尾的CALL,CALL中的THIS是FN1.CALL,先把FN1.CALL中的THIS改为FN2,然后让FN1.CALL执行
    * 第二次CALL执行,方法中的THIS已经被上一次修改为FN2了,所以参考“THIS是谁就执行谁”的标准,执行的是FN2
    */
    fn.call.call(1)//报错
    

    手写call

    Function.prototype.call = function() {
      let [thisArg, ...args] = [...arguments];
      if (!thisArg) {
          //context为null或者试undefined
          thisArg = typeof window === 'undefined' ? global : window;
      }
      //this指向试当前函数func
      thisArg.func = this;
      //执行函数
      let result = thisArg.func(...args);
      delete thisArg.func;
      return result;
    }
    

    bind预处理this,不执行函数

    bind:语法和call一模一样,唯一的区别在于立即执行还是等待执行

  • fn.call(obj,10,20) 改变FN中的THIS,并且把FN立即执行,返回的是fn函数执行的结果

  • fn.bind(obj,10,20) 改变FN中的THIS,此时的FN并没有执行(不兼容IE6~8),把改变this后的函数体返回

    let fn = function (a, b) {
      console.log(this);
    };
    let obj = {name: "OBJ"};
    document.onclick = fn;//=>把FN绑定给点击事件,点击的时候执行FN
    document.onclick = fn();//=>在绑定的时候,先把FN执行,把执行的返回值(UNDEFINED)绑定给事件,当点击的时候执行的是undefined
    //=>需求:点击的时候执行FN,让FN中的THIS是OBJ
    document.onclick = fn;//=>this:document
    document.onclick = fn.call(obj);//=>虽然this确实改为obj了,但是绑定的时候就把fn执行了(call是立即执行函数),点击的时候执行的是fn的返回值undefined
    document.onclick = fn.bind(obj);//=>bind属于把fn中的this预处理为obj,此时fn没有执行,当点击的时候才会把fn执行
    function fe(){
     console.log(this)
    }
    fe.call(1)//=> Number(1)  实例化后的1
    

    apply (传参方式不同)

    apply:和call基本上一模一样,唯一区别在于传参方式

  • fn.call(obj,10,20)

  • fn.apply(obj,[10,20]) APPLY把需要传递给FN的参数放到一个数组(或者类数组)中传递进去,虽然写的是一个数组,但是也相当于给FN一个个的传递