this指向问题

普通函数中的this

非严格模式 this -> window

1
2
3
4
function a() {
console.log(this);
}
a(); // windows

严格模式 this -> undefined

1
2
3
4
5
"use strict"
function a() {
console.log(this);
}
a(); // undefined

call,apply,bind中的this

  • 情况1:this -> Window

    • a.call()
    • a.call(undefined)
    • a.call(null)
    1
    2
    3
    4
    5
    6
    function a() {
    console.log(this);
    }
    // a.call(); // Window
    // a.call(undefined) // Window
    a.call(null) // Window
  • 情况2:传什么,this就是什么

    1
    2
    3
    4
    5
    6
    7
    function a() {
    console.log(this)
    }
    // a.apply("abc"); // String {'abc'}
    // a.call(123); // Number {123}
    const fn = a.bind({x: 1});
    fn(); // {x: 1}

定时器中的this

  • 情况1:定时器 + function this->Window

    这种情况下的this始终指向Window

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    setTimeout(function() {
    console.log(this) // Window
    }, 100)

    function fn() {
    setTimeout(function() {
    console.log(this)
    }, 100)
    }
    // fn() // Window
    fn.call({x: 100}) // Window
  • 情况2:定时器 + 箭头函数 this->上层作用于的this

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    setTimeout(() => {
    console.log(this) // Window
    }, 100)

    // 普通函数的this是Window
    function fn() {
    console.log(this) // Window
    setTimeout(() => {
    console.log(this) // Window
    }, 100)
    }
    fn()

    // 箭头函数定时器的this,指向上层作用于的this
    function fn() {
    console.log(this) // {x: 100}
    setTimeout(() => {
    console.log(this) // {x: 100}
    }, 100)
    }
    fn.call({x: 100})
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class obj{
    fn() {
    setTimeout(() => {
    console.log(this) // obj {}
    },100)
    }
    }
    const o = new obj()
    o.fn()

    image-20230409113704961

箭头函数中的this

  • 情况1:有function作用域的,this是上层作用域的this

    1
    2
    3
    4
    5
    6
    7
    class obj {
    say = ()=> {
    console.log(this) // obj {say: ƒ}
    }
    }
    const o = new obj()
    o.say()
  • 情况2:没有function作用域的,this是Window

    1
    2
    3
    4
    5
    6
    const obj = {
    say: ()=> {
    console.log(this) // Window
    }
    }
    obj.say(); 
    1
    2
    3
    4
    5
    6
    7
    8
    const btn = document.getElementById("btn");
    // btn.onclick = function() {
    // console.log(this) // <button id="btn">test</button>
    // }

    btn.onclick = ()=> {
    console.log(this) // Window
    }

apply ,call , bind的区别

apply和call

call() 与apply()只有一个区别,就是 call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组。 call(); 传递参数类型 直接返回调用结果.

callapply的唯一区别就是,call需要一个个的传可选参数,而apply只需要传一个数组的可选参数。

普通函数的this默认指向Window。所以下面输出==undefined 作者: undefined.==

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const book = {
title: '西游记',
author: '吴承恩',
}

function summary() {
console.log(`${this.title} 作者: ${this.author}.`)
}

summary() //undefined 作者: undefined
summary.call(book) // 西游记 作者: 吴承恩.
summary.apply(book) // 西游记 作者: 吴承恩.

function longerSummary(genre, year) {
console.log(
`${this.title} 作者: ${this.author}. 它是一个 ${genre}${year} 发表.`
)
}
longerSummary.call(book, '小说', 1999) // 西游记 作者: 吴承恩. 它是一个 小说 在 1999 发表.
longerSummary.apply(book, ['小说', 1999]) // 西游记 作者: 吴承恩. 它是一个 小说 在 1999 发表.

// longerSummary.apply(book, '小说', 1999) // this指向问题.html:125 Uncaught TypeError: CreateListFromArrayLike called on non-object
longerSummary.apply(book, ['小说', 1999]) // 西游记 作者: 吴承恩. 它是一个 小说 在 1999 发表.

bind

callapply都是一次性使用的方法 – 如果你调用带有this上下文的方法,它将含有此上下文,但是原始的函数依旧没改变。

有时候,你可能需要重复地使用方法来调用另一个对象的上下文,所以,在这种场景下你应该使用bind方法来创建一个显示调用this全新函数

1
2
const newSummary = summary.bind(book)
newSummary() // 西游记 作者: 吴承恩.

在这个例子中,每次你调用newSummary,它都会返回绑定它的原始this值。尝试绑定一个新的this上下文将会失败。因此,你始终可以信任绑定的函数来返回你期待的this值。

1
2
3
4
5
6
7
8
9
const newSummary = summary.bind(book)
newSummary() // 西游记 作者: 吴承恩.
const book2 = {
title: '1984',
author: 'George Orwell',
}

const newSummary2 = newSummary.bind(book2) // 并没有绑定成功
newSummary2(); // 西游记 作者: 吴承恩.

比较

相同点:

三者都是用来改变函数的上下文,也就是this指向的。

不同点:

fn.bind: 不会立即调用,而是返回一个绑定后的新函数。

fn.call:立即调用,返回函数执行结果,this指向第一个参数,后面可有多个参数,并且这些都是fn函数的参数。

fn.apply:立即调用,返回函数的执行结果,this指向第一个参数,第二个参数是个数组,这个数组里内容是fn函数的参数。

应用场景

  • 需要立即调用使用call/apply

  • 要传递的参数不多,则可以使用fn.call(thisObj, arg1, arg2 ...)

  • 要传递的参数很多,则可以用数组将参数整理好调用fn.apply(thisObj, [arg1, arg2 ...])

  • 不需要立即执行,而是想生成一个新的函数长期绑定某个函数给某个对象使用,使用const newFn = fn.bind(thisObj); newFn(arg1, arg2...)

参考文章

理解JavaScript中的This,Bind,Call和Apply

call, apply, bind 区别及原理


this指向问题
http://example.com/2023/04/07/07.前端小课堂/06.this指向问题/
作者
Deng ErPu
发布于
2023年4月7日
许可协议