javascript-this全面解析

136 阅读3分钟

前言

this 在 JS 是一个很重要的部分,在学习和实战都能经常用到。在前端面试中,this 也是一个经常被问到的考点,它的作用,就是以一个优雅的方式隐式传递了一个对象的引用,提高了代码的复用性,这也是为什么要用 this。重点,就是了解 this 的绑定规则,才能更好的在实战开发中运用。

this 的绑定规则

1>默认规则

函数直接使用不带任何修饰的函数引用进行调用(独立函数调用),this默认绑定全局对象Window

*注:严格模式下,不能将全局对象用于默认绑定,因此this会绑定到 undefined

function foo(){
    var a = 1
    console.log(this.a);//2
}
var a=2
foo()

上面代码 foo 函数在全局直接调用,因此只能使用默认绑定,this指向全局对象window,所以,打印a=2

2>隐式绑定

当函数被某个对象拥有时(函数被引用到对象),此时函数引用有上下文对象,触发隐式绑规则:会把函数中的this绑定到对象(上下文对象)当中

    function foo(){
        console.log(this.a); //2
    }

    var obj = {
        a: 2,
        foo: foo //如果是foo(),就不是引用,不被拥有,this指向window对象,打印undefined
    }
    obj.foo()

*注:对象属性引用链只有上一层或者说最后一层在调用位置起作用

    function foo(){
        console.log(this.a); //42
    }

   obj1 ={
        a :42,
        foo:foo
    }

    obj2 = {
        a:2,
        obj2:obj1
    }
    obj2.obj2.foo()

以上代码,函数foo被引用在obj1中,而obj1被引用在obj2中,调用时还是在最后一层(obj1)中起作用,this指向直接引用自己的对象obj1

3>隐式丢失

多次引用函数,最终函数调用前没有上下文对象,就会造成隐式丢失,从而走默认绑定规则(是一种现象)

    function foo(){
        console.log(this.a); //1
    }
    var a = 1 //全局对象属性
    var obj = {
        a:2,
        foo:foo
    }
    var bar = obj.foo;//隐式丢失
    bar() //即foo()

以上代码,函数foo被对象obj引用后,bar再次引用obj.foo,实际上bar引用的就是foo函数本身,此时bar的调用就是foo的直接调用,是不带任何修饰的函数调用,因此引用默认绑定,从而this绑定到全局对象或者undefined(取决于是否是严格模式)

*注:如果多次引用后,仍然被一个对象所拥有,此时函数引用仍然有上下文对象,就不会发生隐式丢失,就还是隐式绑定

    function foo(){
        console.log(this.a); //3
    }
    var a = 1
    var obj = {
        a:2,
        foo:foo
    }
    var bar = {
        a:3,
        bar:obj.foo
    }
    bar.bar() 

4>显示绑定

通过调用辅助函数,修改函数this的指向,主要有call、apply、bind方法

    function foo(){
        console.log(this.a);
    }

    var obj = {
        a:2
    }

    foo.call(obj) //2
    foo.apply(obj) //2
    var bar = foo.bind(obj) //2
    bar()

区别:

  • call 修改函数的this指向,接受零散的参数
  • apply 修改函数的this指向,参数以数组的形式接受
  • bind 修改函数的this指向,会返回一个新的函数,接受零散的参数
    function foo(x,y){
        console.log(this.a,x+y);
    }

    var obj = {
        a:2
    }

    foo.call(obj,1,2) //2 3
    foo.apply(obj,[1,2]) //2 3


    var bar = foo.bind(obj,2,3)
    bar() //2 5
    
    var bar = foo.bind(obj)
    bar(2,3) //2 5

*注:当调用这三个函数时不传递参数,或者传递null为参数,那么函数的this会指向window

这三种方法在面试时也会经常被要求手写实现,主要就是实现修改this指向,和考虑到传递的参数情况,需要我们手写的函数和它们具有完全一样的功能特征

5>new 绑定

当使用new来调用函数时,即发生构造函数调用时,新的实例对象会被绑定到构造函数的this上,即this指向实例对象

    function foo(a){
        this.a = a
    }
    var bar = new foo(2)
    console.log(bar.a) //2

优先级

new绑定 > 显示绑定 > 隐式绑定 > 默认绑定

箭头函数this指向问题

箭头函数本身没有this这个概念,写在其内部的this,也就是其外部的普通函数的this

    function foo(){
        // this
        var bar = () => {
            var baz = () => {
                console.log(this.a) //相当于直接写在foo里的this
            }
        }
    }