js中的this指向问题

1.产生的问题

  • 1.类中的this指向
  • 2.方法中的this指向
  • 3.匿名函数中的this指向
  • 4.怎么解决this指向问题

2.样例及解决

1.类中的this指向

<script type="text/javascript">
        class Person{
            // 构造器
            constructor(id, name){
                console.log("类中构造器的this指向: ", this)
                this.id = id
                this.name = name
            }
            // 普通方法
            eat(){
                console.log("类中方法的this指向: ", this)
            }
        }
        const p1 = new Person(1, 'lily')
        p1.eat(); // 通过Person实例调用eat方法,其中this是Person的实例
        console.log("===================")
        const temp = p1.eat // 将Person类的特殊属性(方法)eat赋值给temp
        console.log("方法的定义: ", temp)   // 输出方法的定义
        console.log("将Person类内部的eat方法赋值给temp变量,并调用:")
        temp()  // 直接调用temp方法,this是undefined
    </script>

完整输出为如下:

image.png

  • 1.最后一行temp() // 直接调用temp方法,this是undefined输出为什么是undefined,而不是Person实例或者window对象?
    • 原因:因为js在类中自动开启了严格模式("use strict";//禁止this关键字指向全局对象),所以这里的this为undefined。

2.方法中的this指向

<script type="text/javascript">
        function test1(){
            console.log(this)   // 方法中直接输出this指向window对象
        }
        function test2(){
            "use strict";
            console.log(this)   // 加了严格模式,输出this指向为undefined
        }
</script>

输出如下:

image.png

3.匿名函数中的this指向

<script type="text/javascript">
        // 匿名函数测试1
        const temp1 = {
            id: 123,
            temp1: function(){
                console.log("外部this指向: ", this) // 输出Object对象(及js对象)
                return function(){
                    console.log("内部this指向: ", this) // 输出window对象
                }
            }
        }
        console.log("匿名函数定义: ", temp1.temp1())  // 输出外部this并同时输出匿名函数定义
        temp1.temp1()() // 内部的匿名函数调用
        console.log("================")
        // 匿名函数测试2
        const btn = document.getElementById('btn').addEventListener('click', ()=>{
            console.log("监听事件的匿名函数中this指向: ", this)
            btnInner();
            btnInner1();

            (function(){
                console.log("匿名函数中的匿名函数中的this指向: ", this)
            })()
        })
        function btnInner(){
            console.log("匿名函数调用非严格模式中的this指向: ", this)
        }
        function btnInner1(){
            "use strict";
            console.log("匿名函数调用严格模式中的this指向: ", this)
        }
</script>

输出如下:

image.png

4.怎么解决this指向问题

  • 1.全局定义一个变量that,将this赋值给变量that,然后使用(视开发情况而使用)
  • 2.使用call,apply,bind方法解决this指向
  • 1.先说call
    call(this指向, 函数参数, 函数参数...), this指向就是需要把哪个对象实例传进去,函数参数就是调用call方法的那个函数的参数(有点绕口,看例子)。这个例子中myIntroduce有俩个参数,name没有使用this访问,所以可以直接传入参数输出出来,而age使用this去访问的(本来this应该指向objCall,但是此时this却为undefined,这并不是我们所期望的),所以使用call改变this的指向由undefined到objCall就可以访问到age。注意:我这里专门传入第二个参数19,结果却输出13,原因很简单(没有this.age=age),这里的age并没有set到objCall的原型链上。

<script type="text/javascript">
        const objCall = {
            age: 13,
            myIntroduce: function(name, age){
                console.log("我是:", name, ",性别:", this.age)
            }
        }
        const callIntroduce = objCall.myIntroduce
        callIntroduce('约翰', 19)   // this指向window对象, 但是我们期望的是this指向objCall对象
        // 使用call
        callIntroduce.call(objCall, '约翰', 19)
</script>
  • 2.再说apply
    apply(this指向, [函数参数, 函数参数...]),没错,apply和call的区别就是apply的第二个参数是一个数组。(例子如下),从下面这个例子可以看出方法1.apply(this指向另一个对象noObjApply ,[参数1, 参数2]),而在noObjApply中由一个与方法1同名的方法2,最后的结果是方法1可以使用noObjApply中的属性,而不会直接换到方法2去

<script type="text/javascript">
        const objCall = {
            age: 13,
            myIntroduce: function(name, age){
                console.log("我是:", name, ",性别:", this.age)
            }
        }
        const noObjApply = {
            age: 20,
            myIntroduce: function(name, age){
                console.log("我是noObj名字为:", name, ",性别:", this.age)
            }
        }
        const callIntroduce = objCall.myIntroduce
        callIntroduce('约翰', 19)   // this指向window对象, 但是我们期望的是this指向objCall对象
        // 使用apply
        callIntroduce.apply(objCall, ['约翰', 19])
        callIntroduce.apply(noObjApply, ['约翰', 19])
</script>

输出如下:

image.png

  • 3.最后说bind
    bind(this指向, 参数1, 参数2...),这个例子和前俩个差不多,有一点就是bind方式改变this指向返回一个新的函数,需要自己去调用这个函数。

<script type="text/javascript">
       const objCall = {
            age: 13,
            myIntroduce: function(name, sex, age){
                console.log("我是:", name, ",性别:", sex, ",年龄:", this.age)
            }
        }
        const noObjApply = {
            age: 20,
            myIntroduce: function(name, sex, age){
                console.log("我是noObj名字为:", name, ",性别:", sex, ",年龄:", this.age)
            }
        }
        const callIntroduce = objCall.myIntroduce
        callIntroduce('约翰', '男', 19)   // this指向window对象, 但是我们期望的是this指向objCall对象
        const changeThisP = callIntroduce.bind(objCall, '约翰', '男', 19)   // bind返回一个函数,不会立即执行
        changeThisP()
        const changeThisP1 = callIntroduce.bind(noObjApply, '约翰', '男', 19)   // bind返回一个函数,不会立即执行
        changeThisP1()
        const changeThisP2 = callIntroduce.bind(objCall, ['约翰', '男', 19])   // bind返回一个函数,不会立即执行
        changeThisP2()
        const changeThisP3 = callIntroduce.bind(noObjApply, ['约翰', '男', 19])   // bind返回一个函数,不会立即执行
        changeThisP3()
</script>

输出如下:


image.png

3.问题总结

  • 1.call和apply修改this指向都是调用的时候执行,唯一的不同点是传参问题,call的第二个参数以后直接传递参数,apply的第二个参数却是一个数组,调用方式都是及用及调。
  • 2.bind与call和apply的不同
    • 1.参数问题,bind的第二个参数如果为数组,需要改变this指向的方法内部使用参数的时候需要下标访问,如果第二个参数不是数组,而是多个参数,参数传递方式和call的一样。
    • 2.调用问题,call和apply都是直接调用的,而bind则是返回一个函数,需要的时候再去调用。
  • 3.附加: 还有一个箭头函数()=>{},箭头函数中没有自己的this,如果在箭头函数中使用了this这个关键字(使用不报错),箭头函数就会找其外侧函数的this作为箭头函数的this去使用。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容