彻底搞懂this指向问题

282 阅读4分钟
介绍

每一个函数在运行的时候都会创建一个独有的活动对象

  • 在活动对象中包含函数的参数,以及函数函数的调用模式内部的声明数据
  • 除了定义参数与声明的变量与函数外,在这个活动对象上还存在一个叫做上下文(context)的东西
  • 函数的七种调用模式

在js中this的意义:

  1. 记住 this在什么情况下表示什么东西
  2. 通过对代码分析,我们可以定位是哪一种情况,能分析出是哪种意义
  3. 总结: 函数内部的this,是由函数调用的时候来确定其指向的

函数的七种调用模式

  1. 函数模式:this表示全局对象(浏览器中是window,在node中是global)
  2. 构造器(constuctor)模式:this表示刚刚创建出的对象
  3. 方法(method)模式:this表示引导方法调用的对象
  4. 上下文(context)模式:this可以使用参数来动态的描述,每次调用可以传入不同的对象作为this(动态绑定)
  5. bind模式:this与上下文模式类似,也是通过参数来确定,一开始就绑定,然后在使用的时候就不需要绑定,每次使用的时候都是最开始绑定的那个this(静态绑定)
  6. 作为事件的处理函数: 触发该事件的对象
  7. 作为定时器的参数: 指向window

举例

  1. 函数模式
    • 特点:独立的运行,调用语法格式前没有任何引导数据
    function foo(){
            console.log("函数模式")
    }
    foo()
  1. 构造器模式
    • 语法 new 构造函数()
    • 先使用 new 运算符 分配内存空间,在js中就是创建对象(一个空的对象,一个具有原型结构的对象)
      • 空对象表示没有自己的任何成员
      • 具有原型结构,该对象的原型(__ prop__)是Person.prototype
    • 调用构造函数
      • 创建活动文对象
      • this创建的对象的引用会作为活动对象中的上下文对象被引用
      • 预解析
      • 解释执行等等
    function Person(){}
    var p = new Person()
  1. 方法调用模式
    • 一个函数作为对象的一个成员,由对象引导调用,这个调用就是方法调用
    • 表现就是调用前有一个引导数据:满足通过xxx访问到方法名调用
    • demo4 需要特别注意下
    //demo1
    var obj1 = {
        say:function(){console.log(this)}
    };
    obj1.say();
    
    //demo2
    function foo(){
        console.log("这是什么调用",this)
    }
    var obj2 = {name:obj2};
    obj2.fn = foo;
    obj2.fn(); //方法调用,通过obj2引导的函数再调用
    foo();//函数

    //obj.fn 与 foo是什么关系呢?
    //他们是完全一样的东西,但执行结果不同,即this不同
    
    demo3
    var arr = [];
    arr.push(foo);
    var fn = arr[0];
    fn();//是什么调用呢? 函数调用模式
    arr[0]()//这又是什么调用呢? **方法调用模式** arr的引导
    
    //demo4
    var obj3 = {
        name:"deno4",
        say:function(){console.log("这是什么调用",this)}
    };
    obj3.say();//方法
    var fn = obj3.say;
    fn();//函数
    //特别
    var f;
    (f = obj3.say)();//面试题遇到过 函数模式
    (obj3.say)()//方法
    
    //词法分析 运行原理
    //赋值运算 分为:
    //1. 将 等号 右边的值取出来(求出),取出的东西与原本的东西并不相同
    //2.将取出的数据 存储到 等号 左边 的变量表示的容器中
    //赋值表达式的值就是取出的那个值
    //所以此时就是在调用取出的那个东西
  1. 上下文模式
    • 函数名.call(上下文,参数1,参数2,...) 可以任意个参数
    • 函数名.apply(上下文,[参数1,参数2,...]) 最多二个参数
    • 无论是函数正常调用,还是call调用,还是apply调用其实都是在调用函数
    • 函数执行的时候会创建活动对象,活动对象中会存在一个上下文 就是函数中的this
    • call和apply方法的第一个参数就是决定上下文的东西也就是this
      1. 如果传入的是 引用类型,除了null外就是表示this
      2. 如果传入的是基本类型(数字,布尔,字符串),它们会自动转为包装类型
      3. 如果传入的是空(null,undefined),那么上下文就是全局对象
      4. 如果call与apply不传入任何参数,就相当于函数调用
      5. 如果如果call与apply只传入一个参数,那么相当于方法调用
      6. call与apply除了第一个参数外,其他的所有参数都是与原函数参数相对应的,只是形式不同
    • call从第二个参数开始,描述的就是正常函数调用时需要提供的参数
    • apply的第二个参数是数组的形式,用数组的形式描述正常函数的参数
    • 运用:主要是借用与展开
      1. 比如Array.prototype.slice.call(伪数组)就能够借助数组的slice方法给伪数组使用等
      2. 比如借用Math.max()求数组的最大值 Math.max.apply(Math,arr)
    function foo(){
        console.log("这是什么调用",this)
    }
    foo(); //this-->window
    foo.call(); //this --> window
    foo.call({name:'obj1'});//this --> {name:'obj1'}
    foo.apply();//this-->window
    foo.apply({name:'obj2'});//this --> {name:'obj2'}
    //
  1. bind模式
    • func.bind(context, arg1, arg2, ...)//返回的是已经绑定this的新函数
    document.querySelectorAll('a')
    var $selectAll = document.querySelectorAll.bind(document);
    $selectAll('a')