JavaScript高架高级(四)---这可能是你看过的最完整的this指向

 前言

简单阐述

  • 前端的初学者在学习Javascript中this指向的时候经常都会一头雾水,尤其是在ES6箭头函数出现之前。

  • this指向之所以容易让人头疼,原因在于 this 是在代码执行时根据环境和情况不同才决定绑定为什么值。

  • 所以本篇文章主要是介绍和总结了各种情况下的this指向,如果文中有写的不对的地方或者明显错误,还请不吝指出互相交流。如果对你有帮助,也请不要吝啬手中的点赞~谢谢

在开始之前,还是需要介绍this的一些基础知识,具体如下:

  1. this 是javascript中的一个关键字,并非是一个变量。
  2. this 关键字指向的是一个对象,而指向哪个对象,或者是这个对象的值是什么取决于使用(调用)的方式和环境。
  3. this 指向的值是可以通过手动方式去改变的,比如call、bind、apply方法。
  4. this 在严格模式和非严格模式下也会有差别。

this的作用

  • 从某些角度来说,开发中我们没有this,也是有解决方案的,但是如果真的没有this,会让我们编写代码十分不方便。
  • 例如:
         // 如果没有this,我们更改了obj的命名 
         // 那么下面的obj.name 都需要进行修改
        var obj = {
            var name = "malong"
            eating: function() {
                 console.log(obj.name + "吃东西")
            }
            studying: function() {
                 console.log(obj.name + "学习")
            }
        }
    
    

    this的指向

  • 在多数情况下,this出现在函数中,正常开发,通常都是在函数中使用this
    • 所有的函数在被调用时,都会创建一个执行上下文FEC
    • 这个上下文中记录着函数的调用栈、AO对象等;
    • this也是其中的一条记录;
  • 函数在调用时,JavaScript会默认给this绑定一个值
  • this的绑定和定义的位置(编写的位置)没有关系
  • this的绑定和调用方式以及调用的位置有关系

this绑定方式1:默认绑定

独立的函数调用我们可以理解成函数没有被绑定到某个对象上进行调用,这样this就是默认绑定

this是在运行时被绑定的

// 案例1 默认绑定
function foo() {
    console.log(this)
}

// 案例2 默认绑定
function test1() {
    console.log(this)
    test2()
}

function test2() {
    console.log(this)
    test3()
}

function test3() {
    console.log(this)
} 
test1()

// 案例3 默认绑定
function bar() {
    func()
}

var obj = {
    name: "malong",
    foo: function() {
        console.log(this)
    }
}

bar(obj.foo)

this绑定方式2:隐式绑定

  • 必须在调用的对象内部有一个对函数的引用(比如一个属性)
  • 如果没有这样的引用,在进行调用时,会报找不到该函数的错误
  • 正是通过这个引用,间接的将this绑定到了这个对象上
  • 调用方式是通过某个对象进行调用的,隐式绑定
// 案例1 隐式绑定
function foo() {
    console.log(this)
}

var obj = {
    name: "malong",
    foo: foo
}

obj.foo()

// 案例2 隐式绑定
function bar() {
    console.log(this)
}

var obj1 = {
    name: "malong1",
    bar: bar
}

var obj2 = {
    name: "malong2",
    obj1: obj1
}

obj2.obj1.bar()

// 案例3 隐式绑定
function baz() {
    console.log(this)
}

var obj3 = {
    name: "malong",
    foo: baz
}

var fn = obj3.foo;
fn()

this绑定方式3:显示绑定

  • bind
    • 形参第一个参数是对象,第二个参数是列表,返回值是一个函数。
  • call
    • 形参第一个参数是对象,第二个参数是列表
  • apply
  • 形参第一个参数是对象,第二个参数是数组 通过call绑定this
        function foo() {
            console.log(this)
        }
        
        foo.call(window)
        foo.call({name: "malong"})
        foo.call(123)
    

    如果我们希望一个函数总是显示绑定到一个对象上,而不用向上面那样写多次:

        function foo() {
            console.log(this)
        }
        
        var obj = {
            name: "123"
        }
        
        var baz = foo.bind(obj);
        baz()
    

    this绑定方式4:new绑定

  • JavaScript中的函数可以当做一个类的构造函数来使用,也就是使用new关键字。
    function Person(name) {
        console.log(this)
        this.name = name
    }
    
    var person = new Person("malong")
    

    this的绑定过程:

  • 创建一个新的对象
  • 将构造函数的作用域赋值给新对象(构造函数.prototype === 对象.__ proto__)
  • 执行构造函数中的代码(this绑定在这个步骤完成)
  • 返回这个对象(内部有一个隐式的 return this)

this绑定优先级

new > 显示绑定(apply,call,bind) > 隐式绑定 > 默认

扩展【一】:this规则之外

无视显示绑定规则:

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

var obj = {
    name: "malong"
}

foo.call(obj)
foo.call(null)
foo.call(undefined)

var baz = foo.bind(null)
baz()

间接函数引用:

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

var obj1 = {
    name: "obj1",
    foo: foo
};

var obj2 = {
    name: "obj2"
}

obj1.foo();
(obj2.foo = obj.foo)();


箭头函数:

  • 箭头函数没有this,它的this由运行时外层作用域的this决定
  • 箭头函数没有arguments
  • 箭头函数不能new
  • 
        var name = "window";
        var obj = {
          name: "obj",
          getData: function() {
           // 会默认绑定,所以这里的this是 obj
            setTimeout(() => {
              console.log(this)
            }, 100)
          }
        }
        // 如果getData也是一个箭头函数,那么this会是window
        obj.getData()
        
    

    扩展【二】:this面试题

  •     var name = "window"
        
        var person = {
            name: "person",
            sayName: function() {
                console.log(this.name);
            }
        };
        
        function sayName() {
            var sss = person.sayName;
            sss();
            person.sayName();
            (person.sayName)();
            (b = person.sayName)();
        }
        sayName()
    
        var name = "window";
    
        var person1 = {
          name: "person1",
          foo1: function () {
            console.log(this.name)
          },
          foo2: () => console.log(this.name),
          foo3: function () {
            return function () {
              console.log(this.name)
            }
          },
          foo4: function () {
            return () => {
              console.log(this.name)
            }
          }
        }
    
    
        var person2 = { name: "person2" }
    
        person1.foo1();
        person1.foo1.call(person2);
    
        person1.foo2();
        person1.foo2.call(person2)
    
        person1.foo3()()
        person1.foo3.call(person2)()
        person1.foo3().call(person2)
    
        person1.foo4()()
        person1.foo4.call(person2)()
        person1.foo4().call(person2)
    
     var name = 'window'
        function Person(name) {
          this.name = name;
          this.obj = {
            name: 'obj',
            foo1: function () {
              return function () {
                console.log(this.name)
              }
            },
            foo2: function () {
              return () => {
                console.log(this.name)
              }
            }
          }
        }
    
        var person1 = new Person('person1')
        var person2 = new Person('person2')
    
        person1.obj.foo1()()
        person1.obj.foo1.call(person2)()
        person1.obj.foo1().call(person2)
    
        person1.obj.foo2()()
        person1.obj.foo2.call(person2)()
        person1.obj.foo2().call(person2)
    

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值