美文网首页
JavaScript学习笔记(五)this关键字

JavaScript学习笔记(五)this关键字

作者: 机智的akunda | 来源:发表于2020-03-16 11:05 被阅读0次

    JavaScript中this关键字的指向是一个让初学者(比如我...)很头疼的问题。本文的内容主要出自阮一峰老师JavaScript教程this 关键字一节,读完受益良多,在此简要总结,方便日后回顾。

    1. this关键字的意义

    在理解this指向问题之前,需要先了解设计这个关键字的初衷。在JavaScript中,存储引用类型(广义的对象)值的变量中存放的是地址。访问对象属性时,首先从变量获取对象地址,然后再从该地址读取对应的属性,此时属性所处的上下文(环境)就是当前对象。

    如果对象的属性是一个函数,那么该属性本身存储的是函数的地址。调用对象方法时,先从变量获取对象地址,再从对象地址中读取方法地址,再调用函数,函数调用时的环境也是当前对象。

    函数作为一个值,可以在不同的上下文中执行,而this关键字的作用正是用来指代函数执行时所处的环境。

    const obj = {
        a: 1,
        b: function() {
            console.log(this.a)
        }
    }
    
    obj.b() // 1
    

    上面这段代码的执行过程可以描述为:

    • 先通过变量obj获取对象地址
    • 再从对象地址获取函数b的地址
    • 通过函数b的地址调用函数,this指向当前环境,也就是obj对象,因此this.a就是1

    2. this关键字的指向

    this指向的就是引用this的环境(或者称之为对象)。

    2.1 全局作用域

    在全局作用域中,this指向window对象(浏览器环境)。还是上面的例子:

    const obj = {
        a: 1,
        b: function() {
            console.log(this.a)
            console.log(this)
        }
    }
    window.a = 2
    const temp = obj.b
    temp() // 2 window {...}
    obj.b() // 1 {a: 1, b: f}
    

    可以看出,如果将obj.b函数赋值给变量temp,则变量temp存储了该函数的地址,调用temp()是在全局环境中直接获取了函数的地址,因此this指向window对象。而通过对象obj调用函数时,函数内部的this指向obj

    2.3 构造函数

    构造函数中的this指向通过new关键字调用构造函数的实例对象。

    2.3 对象的方法

    对象方法中的this指向方法执行时所在的对象。
    如果对象的属性还是一个对象,那么嵌套的对象的方法中的this指向嵌套的对象,而不是最外层的对象。看下面这个例子:

    const obj = {
        a: 1,
        b: {
            m: function() {
                console.log(this.a)
            }
        }
    }
    obj.b.m() // undefined
    

    调用obj.b.m()时,其中的this指向属性b这个对象,因此this.aundefined

    2.4 箭头函数

    ES6新增了箭头函数语法。箭头函数没有this,如果在箭头函数中使用this,就当做一个普通的变量,会按照作用域规则从当前作用域向上级作用域逐级查找,直到全局作用域对象window

    const obj = {
        a: 1,
        test: function() {
            console.log(this.a)
        }
    }
    obj.test() // 1
    

    正常函数的this指向函数调用时的环境。

    window.a = 2
    const obj = {
        a: 1,
        test: () => {
            console.log(this.a)
        }
    }
    obj.test() // 2
    

    箭头函数中的this就是一个普通变量,由于test()方法被调用时处于obj的环境且找不到this变量,会继续向上一级作用域也就是全局作用域查找,全局作用域中this就是window对象,因此最终打印结果为2。

    3. 改变this指向

    this随函数执行环境动态切换,虽然具有极强的灵活性,但也造成了this指向不明朗的困惑。在需要固定this指向的时候,可以通过以下三个定义在Function.prototype中的方法。

    3.1 call()方法

    基本用法为func.call(thisValue, arg1, arg2, ...)。其中,第一个参数是函数func执行时其内部this指向的作用域对象,剩余参数可选,为传入函数func的参数。
    call()方法的一个重要的应用是调用对象的原生方法,例如将字符串转换为数组:

    Array.prototype.slice.call('123') // ["1", "2", "3"]
    

    以及将类数组对象转换为真正的数组:

    Array.prototype.slice.call({0: 'a', 1: 'b', 2: 'c', length: 3}) // ["a", "b", "c"]
    

    3.2 apply()方法

    用法与call()类似,唯一的区别在于函数的参数以数组形式传入,即func.apply(thisValue, [arg1, arg2, ...])

    3.3 bind()方法

    call()apply()方法在被函数调用之后,会立即执行函数。但有时候我们不需要函数被立刻执行,而是需要返回一个新函数,可以使用bind()方法。看下面的例子:

    const counter = {
        count: 0,
        inc: function() {
            this.count++
            console.log(this)
        }
    }
    
    function add(callback) {
        callback()
    }
    
    add(counter.inc) // Window {...}
    counter.count // 0
    

    counter对象的inc方法作为回调函数传入add函数,在调用add函数时,inc方法内部的this指向的是add方法被调用时所处的环境,也就是window对象。因此counter对象的count属性还是0。

    此时可以使用bind()inc方法内部的this指向固定为counter对象:

    add(counter.inc.bind(counter)) // {count: 1, inc: ƒ}
    counter.count // 1
    

    需要注意的是,函数没调用一次bind()方法,就返回一个新函数。如果这个新函数在多个地方被使用,应该提前将其缓存下来,而不是在每一个使用到的地方通过bind()生成。

    相关文章

      网友评论

          本文标题:JavaScript学习笔记(五)this关键字

          本文链接:https://www.haomeiwen.com/subject/nhawehtx.html