JavaScript中的this关键字


this 指向

this 指向基本原则

⭐ this 的取值是在函数执行的时候确定的,不是在函数定义的时候确定的,与闭包相反。 this 指向调用它所在方法的那个对象

  • 普通函数

普通函数的 this,指向 windows (非严格模式)

function fn1() { 
    console.log(this)
}
fn1()   // window
  • 一般对象和类

对象中的普通方法,this 指向该对象本身

const zhangsan = {
    name:'张三',
    sayHi(){
        console.log(this)     // this是当前对象 => obj
    },
}
zhangsan.sayHi()

打印如下👇
在这里插入图片描述

类方法,this 指向类实例

class Person{
    constructor(name){
        this.name = name
    }
    sayName(){
        console.log(this)  // this指向执行时对应的实例
        console.log(this.name)  
    }
}
const A = new Person('jack')
A.sayName()

打印如下👇

在这里插入图片描述

  • 复杂对象的 this

有的时候(大多数是在刷题中,实际开发并不常见),this 的各种挂载会让人摸不到头脑,这种时候要记住 this 指向的原则:this 的指向是在执行时决定的,而非定义时

示例一

const zhangsan = {
    name:'张三',
    printName1(){
        console.log(this.name)  
    },
}
const lisi = {
    name:'李四',
    printName2(){
        zhangsan.printName1()   // 代码执行处
    },
}

zhangsan.printName1() // 张三
lisi.printName2() // 张三
  • 打印一:结果是 ‘张三’,它是一个普通的对象方法
  • 打印二:虽然看起来执行的是lisi的方法,但是函数实际执行的是zhangsan.printName1,因此输出的是 ‘张三’

示例二

var zhangsan = {
    name:'张三',
    printName: function(){
        console.log(this.name)  
    },
}
var lisi = {
    name:'李四',
    printName: function(){
      var fn1 = zhangsan.printName   
        fn1() // 函数执行
    },
}

var name = '王五'
var fn2 = zhangsan.printName

zhangsan.printName()
lisi.printName()
fn2()

执行后打印如下👇
在这里插入图片描述

  • 打印一:这是一个普通的对象方法,打印结果是 ‘张三’
  • 打印二:这是一个容易混淆的地方:fn1虽然是在 lisi 这个对象里定义的,但是在执行的时候,fn1本身其实没有挂载任何对象,它已经是一个挂载在 window 上的方法,因此会打印出全局对象中的name
  • 打印三:与打印二类似—— fn2 指向的实际是个 function() { console.log(this.name) }, fn2此时已经挂载在全局对象

严格模式与非严格模式

  • 非严格模式下全局作用域函数中的this指向window对象
  • 严格模式下全局作用域中函数中的this是undefined
  • html中加入 use strict 会开启严格模式,否则默认非严格模式,当使用ES6模块化(let const),会自动开启严格模式。

明确的this指向——定时器、立即执行函数

在两种特殊情境下,this 会直接指向 window:

  • 定时器 setTimeoutsetInterval中的普通函数(非箭头函数)
  • 立即执行函数
let zhangsan2 = {
    name:'张三2',
    printName: function(){
        console.log(this.name)  
    }(),
    printName2: function(){
      setTimeout(function() {
        console.log(this.name) 
      },100)
        
    },
}

zhangsan2.printName2()

输出如下👇
在这里插入图片描述

  • 打印一:创建zhangsan2对象时,会自动执行立即执行函数,它的this指向window
  • 打印二:定时器中的普通函数this指向window

改变this指向的操作

箭头函数

箭头函数的this会向它执行的上一级作用域 寻找

var name = '张三1'
const zhangsan = {
    name:'张三2',
    fn1: function(){
        console.log(this.name) ;
        setTimeout(()=>{       
            console.log(this.name) 
        }, 100)
    },
    fn2: ()=>{
        console.log(this.name)
    }
}

zhangsan.fn1() // 张三2 张三2
zhangsan.fn2() // 张三1
  • 打印一:定时器中的箭头函数使它内部执行时跳出当前作用域,效果就与第一个打印相同了
  • 打印二:箭头函数跳出zhangsan这个对象,找到了window对象

call、apply、bind、

call、apply 、 bind,都可用来改变函数的 this 指向

  • call 和 apply 之间的区别不大,call 需要将目标函数的入参逐个传入fn.call(target, arg1,arg2…) ,apply需要参数以数组形式传入fn.apply(target, [ arg1,arg2… ])。它们在改变 this 指向的同时,也会同时执行目标函数
  • bind 需要将目标函数的入参逐个传入fn.bind(target, arg1,arg2…) ,它只改变 this 指向,不执行函数
  • call、apply、bind使用示例
        const name = 'global'

        function fnC(a, b, c) {
            console.log(this.name)
            console.log(a, b, c)
        }

        function fnA(a, b, c) {
            console.log(this.name)
            console.log(a, b, c)
        }

        function fnB(a, b, c) {
            console.log(this.name)
            console.log(a, b, c)
        }

        const objC = {
            name: 'Call'
        }
        const objA = {
            name: 'Apply'
        }
        const objB = {
            name: 'Bind'
        }

        // call使用
        fnC.call(objC, 'c','a','ll')
        // apply使用
        fnC.apply(objA, ['a','pp','ly'])
        // bind使用
        const fn = fnB.bind(objB, 'b','i','nd')
        fn()

在这里插入图片描述
进阶——手动实现call、apply、bind(简易版)

        // call与apply
        Function.prototype.myCall = function (target, ...args) {
            // step1: 把函数挂到目标对象上, this 就是要改造的目标函数)
            target.func = this
            // step2: 执行函数,利用扩展运算符将数组展开
            target.func(...args)
            // step3: 删除 step1 中挂到目标对象上的函数
            delete target.func
        }

        Function.prototype.myBind = function (target, ...args) {
            // step1: 记录myBind函数的this
            const self = this
            // step2: 改变函数指向,但不执行
            return function () {
                return self.call(target, ...args)
            }
        }


        fnC.myCall(objC, 1, 2, 3)
        const fn = fnB.myBind(objB, 4, 5, 6)
        fn()

在这里插入图片描述

在上面我们利用ES6的剩余参数,实现了手动实现call、apply、bind函数,对于bind函数的实现,要注意它返回的是一个改变了this指向的待执行函数

总结

this 指向

  • this 指向基本原则
  • 严格模式与非严格模式
  • 明确的this指向——定时器、立即执行函数

改变this指向的操作

  • 箭头函数
  • call、apply、bind、
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值