【前端基础】函数的this到底指向哪里?

74 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天

函数是什么

JS中的函数是对象。对象拥有一个连接到原型的隐形链接,而函数对象连接到Function.prototype,Function.prototype连接到Object.prototype。每个函数在创建时会附加两个隐藏属性:函数的上下文和实现函数行为的代码。函数与对象的不同之处在于可以被调用。

函数在创建时也会随之配一个prototype属性,prototype属性的值是拥有一个contructor属性的且contructor属性的值即为该函数的对象。

image.png

函数中的this

调用函数时,传入函数的参数除了函数声明时定义的形参,还有两个附加参数:this和arguments,this的值取决于函数的调用模式。ES5 引入了bind方法来设置函数的 this值,而不用考虑函数如何被调用的。ES6 引入了箭头函数,箭头函数不提供自身的 this 绑定(this 的值将保持为闭合词法上下文的值)

函数的调用模式有以下几种:

  • 方法调用模式
  • 函数调用模式
  • 构造器调用模式
  • apply/call 调用模式

(1) 方法调用模式

当函数作为对象的属性时,就称这个函数为方法。当调用方法时,this被绑定到所属的对象上面,绑定操作发生在方法调用时。方法通过this实现对读取对象的属性以及操作对象。

const myObject = { 
    value: 0, 
    increment: function(inc) { 
        this.value += typeof inc === 'number' ? inc : 1 
    } 
} 
myObject.increment() 
console.log(myObject.value) // 1 
myObject.increment(2) 
console.log(myObject.value) // 3

(2)函数调用模式

当函数不作为对象的属性时,就是默认的函数调用模式,调用时this被绑定到全局对象(在浏览器中全局对象是window,node中是globalThis)

function f1() {
    return this
}

f1() === window // true

const myObject = {
    value: 1,
}

myObject.double = function(){
    ...
    const helper = function(){
        this.value = this.value * 2
    }
    ...
    helper()
}

let value = 1
myObject.double()
console.log(myObject.value) // 1
console.log(value) // 2

即使是调用方法内部的函数,this仍然被绑定到全局对象。于是方法不能利用内部的函数为自己工作,因为内部函数被绑定到了全局对象,因此不能共享方法对对象的访问权利。

解决方法:方法中定义一个变量指向this(this指向对象),那么内部函数就可以通过该变量访问到对象。

const myObject = {
    value: 1,
}
myObject.double = function(){
    ...
    const that = this
    const helper = function(){
	that.value = that.value * 2
    }
    ...
    helper()
}
myObject.double()
console.log(myObject.value) // 2

(3)构造器调用模式

如果在调用函数时前面加上关键字new,那么该函数就作为构造器函数,会返回一个新对象,并且函数中的this指向创建的新对象。

const Quo = function(s){
    this.status = s
}

const myQuo = new Quo("hello")
console.log(Quo.status) // hello

(4)apply/call 调用模式

apply方法与call方法允许我们选择this的值。apply方法接收2个参数,第一个是要绑定给this的值,第2个参数是一个参数数组。call方法与apply方法类似,不同的是call接收一个参数列表,而 apply方法接收一个参数的单数组。

const arr = [1,2,3] 
arr.push.apply(arr, [4,5,6]) 
console.log(arr) // [1,2,3,4,5,6] 
arr.push.call(arr, 7,8,9) 
console.log(arr) // [1,2,3,4,5,6,7,8,9]

应用apply/call,我们可以调用其他对象中的方法

const Quo = function(s){ 
    this.status = s 
} 
Quo.prototype.getStatus = function(){ 
    return this.status 
} 
const myObject = { 
    status: "It's OK" 
} 
const status = Quo.prototype.getStatus.apply(myObject) 
console.log(status) // "It's OK"