JavaScript函数中this的指向

75 阅读3分钟

this到底指向什么?

我们先来看一个问题:定义一个函数,采用不同的方式去调用,在这个函数中的this指向确实完全不同的结果

function foo(name) {
      console.log("foo函数:", this);
}

//1.直接调用
foo() //window

//2.通过对象调用
var obj = {
       name: 'obj',
       bar: foo 
}
obj.bar() // obj

//3.通过call/apply调用
foo.call('abcd') // String {'abcd'}

这个案例给我们的启示:

  1. 函数在调用时,JavaScript会默认给this绑定一个值
  2. this的绑定和定义的位置(编写的位置)没有关系
  3. this的绑定和调用方式以及调用的位置有关系
  4. this是在运行时绑定的

this的绑定规则:

  • 规则一:默认绑定
  • 规则二:隐式绑定
  • 规则三:显示绑定
  • 规则四:new绑定

规则一:默认绑定

独立的函数调用使用的是默认绑定

  • 独立的函数调用我们可以理解成函数没有被绑定到某个对象上进行调用;
// 1.普通函数被独立调用
function foo() {
    console.log(this);
}
foo() //window

// 2.函数定义在对象中,但是独立调用
var obj = {
    name: 'pak',
    bar: function () {
        console.log("bar:", this);
    }
}
var baz = obj.bar
baz() //window

// 3.高阶函数
function test(fn) {
    fn() //window
}
test(obj.bar)
  • 注意:在严格模式下,独立调用的函数中this指向的是undefined

规则二:隐式绑定

通过某个对象发起的函数调用使用的是隐式绑定,this指向的是这个发起的对象

// 案例一:
function foo() {
    console.log("foo函数:", this);
}

var obj = {
    name: 'pak',
    bar: foo
}
obj.bar()
// 案例二:
function foo() {
    console.log("foo函数:", this);
}

var obj1 = {
    name: 'obj1',
    foo: foo
}

var obj2 = {
    name: 'obj2',
    obj1: obj1
}
obj2.obj1.foo() //obj1
// 案例三:这里属于默认绑定
function foo() {
console.log("foo函数:", this);
}
var obj = {
name: 'pak',
foo: foo
}
var bar = obj.foo
bar() //window

规则三:new绑定

  • 使用new关键字发起函数调用,可以把该函数做一个类的构造函数来使用

  • 使用new关键字来调用函数,会执行以下操作:

    1. 创建一个全新的对象
    2. 这个新对象会被执行prototype连接
    3. this指向这个全新的对象
    4. 执行函数体的代码
    5. 如果函数没有返回其他对象,那么默认返回这个新对象
function foo() {
    this.name = 'pak'
    console.log(this)
}
new foo()

规则四:显示绑定

通过callapplybind发起的函数调用称之为显示绑定

  • JavaScript所有函数都可以使用callapply方法
    • 第一个参数是相同的,要求传入一个对象,this会指向这个对象
    • 后面的参数 apply为数组, call为参数列表
function foo(name, age, height) {
    console.log("foo函数被调用:", this);
    console.log("参数:", name, age, height);
}

foo.apply(obj, ['pak', 18, 1.88])
foo.call(obj, "james", 25, 2.05)
  • 如果希望一个函数总是显示的绑定到一个对象,可以使用bind方法
    • bind()方法返回一个新的绑定函数:exotic function object(怪异函数对象)
    • 在bind函数被调用时,这个新函数的 this 指向 bind() 的第一个参数,而剩余的参数将作为函数的新的参数,供函数调用
    function foo(name, age, height) {
      console.log("foo:", this);
      console.log("foo参数:", name, age, height);
    }

    var obj = { name: 'pak' }
    
    // 需求:调用foo时,总是绑定obj给this
   
    // 1.基本使用, 不传参数
    var baz = foo.bind(obj)
    baz()

    // 2. 传入参数
    baz("kebe", 34)

    // 3.如果使用bind时,传入了参数,那么返回的函数,都会包含该参数
    baz = foo.bind(obj, "kobe", 34)
    // 传入参数会传入后面的形参
    baz('2.12')

至此,JavaScript中this的绑定方式为以上四种,但是在JavaScript中的内置函数的this指向的是什么呢?

内置函数的this绑定

    // 1.定时器
    setTimeout(function () {
      console.log("定时器:", this); //window
    })

    // 2.事件绑定
    var btnEl = document.querySelector("button")
    btnEl.onclick = function () {
      console.log("btn点击:", this); //btnEl
    }

    btnEl.addEventListener('click', function () {
      console.log("btn点击:", this); //btnEl
    })

    // 3.forEach,第二个参数可以指定this
    var names = ["abc", "cba", "nba"]
    var obj = { name: 'pak' }
    names.forEach(function (item) {
      console.log("forEach:", this) //三次obj
    }, obj)

规则绑定优先级