this指向问题

90 阅读4分钟

最近写echars组件封装任务中,在整理自定义回调函数中的逻辑时因为this指向问题模糊不清,搞出了许多不生效的动作,浪费些许时间,项目一结束就立马着手把这块基础却很重要的知识做了下回顾,这里参考网上小伙伴以及自己的理解做下结合简单总结
特别注意:除特殊显式绑定外,查看this指向需要看最后执行调用的this的作用域而非定义时的作用域


一.隐式绑定-this指向window

1.全局环境下直接使用this-this指向window

console.log(this) // window

2.普通函数直接调用-this指向window

function foo(){
  console.log(this) // window
}
foo()

3.普通函数嵌套使用-this指向window

let obj = {
    name:'huan',
    age: 16 ,
    foo:function(){
        console.log('父',this) // { name: 'huan', age: 16, foo: [Function: foo] }
        function test(){
            console.log('子',this) // window
        }
        test()
    }
}
obj.foo()

4.立即执行函数ITIF-this指向window
(1).全局执行

(function(){
    console.log(this) // window
})()

(2).嵌套执行

let obj = {
    foo: function(){
        (function(){
            console.log(this) // window
        })()
    }
} 
obj.foo()

5.闭包-this指向window

let obj = {
    name:'huan',
    age:15,
    foo:function(){
        console.log('1',this) // { name: 'huan', age: 15, foo: [Function: foo] }
        let a = this.name
        return function(){
            console.log('2',this) // window
            return a
        }
    }
}

let fn = obj.foo()
console.log('3',this) // window
fn()

5.对象绑定转移-this指向window

function foo(){
    console.log(this)
}
let obj = {
      name:'huan',
      foo:foo
}
let foo2 = obj.foo // 只是进行了函数绑定,没有执行
foo2() // 真正执行时没有绑定对象 window

6.参数传递-this指向window

function foo(){
    console.log(this) // // window
}
function bar1(fn){
    fn() // 真正执行的时候没有绑定对象,指向window
}
let obj = {
    name:'huan',
    foo:foo
}
bar1(obj.foo)

7.定时器执行-this指向window

在定时器中是作为回调函数来执行的,因此回到主栈执行时是在全局执行上下文的环境中执行的

setTimeout(function(){
    console.log(this) // window
},1000)

二.(隐式绑定)-对象绑定-this指向调用的对象

function foo(){
    console.log(this) // obj2
}
let obj = {
    foo:foo,
    obj2:{
        name:'huan',
        foo:foo
    }
}
// obj.foo()
obj.obj2.foo()

三.(显示绑定)-call()、apply()、bind()、forEach()
1.call()方法
call()方法的第一个参数也是this的指向,后面传入的是一个参数列表(注意和apply传参的区别)。当一个参数为null或undefined的时候,表示指向window(在浏览器中),和apply一样,call也只是临时改变一次this指向,并立即执行。

let obj = {
    name:'huan'
}
function foo(){
    console.log(this) // obj
}
foo.call(obj)

2.apply()方法
apply()接受两个参数,第一个参数是this的指向,第二个参数是函数接受的参数,以数组的形式传入,且当第一个参数为null、undefined的时候,默认指向window(在浏览器中),使用apply方法改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次。

let obj = {
    name:'huan'
}

function foo(){
    console.log(this) // obj
}
foo.apply(obj)

3.bind()方法 bind方法和call很相似,第一参数也是this的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入,call则必须一次性传入所有参数),但是它改变this指向后不会立即执行,而是返回一个永久改变this指向的函数。

let obj = {
    name:'huan'
}

function foo(){
    console.log(this) // obj
}
let fn = foo.bind(obj)
fn()
  1. forEach()方法 forEach接收两个参数,第一个是回调函数,第二个是函调函数中this的指向第一个参数回调函数接收三个参数,第一个是当前遍历的元素,第二个是当前遍历元素的索引,第三个是遍历的数组本身
function foo(el){
    console.log(el)
    console.log(this) // window
}

let arr = [1,2,3]
// arr.forEach(foo) //最终执行环境在window
let obj = {
    name:'huan'
}
arr.forEach(foo,obj)

四.new关键字-this指向当前实例化的对象

function Foo(){
    console.log(this)
}
let fn = new Foo()

五.优先级问题
new绑定 > 显示绑定 > 隐式绑定

function foo(y){
    this.x = y
    console.log(this)
}
let obj = {
    name: 'huan'
}
let foo2 = foo.bind(obj)
foo2(2) //最后执行环境是在window
var foo3 = new foo2(3)
console.log('new',foo3) // new的优先级比较高

六.es6箭头函数-this继承父级(非箭头函数)作用域中的this指向
1.所有绑定规则不适应箭头函数

function foo(){
    console.log('函数内部',this) // { name: 'huan', foo: [Function: foo] }
    let foo2 = ()=>{
        console.log('箭头函数内部',this)  // { name: 'huan', foo: [Function: foo] }
    }
    return foo2
}
let obj = {
    name:'huan',
    foo:foo
}
obj.foo()()

2.显示绑定无效

function foo(){
    console.log('函数内部',this) // { name: 'huan', foo: [Function: foo] }
    let foo2 = ()=>{
        console.log('箭头函数内部',this)  // { name: 'huan', foo: [Function: foo] }
    }
    return foo2
}
let obj = {
    name:'huan',
}
// let foo3 = foo()() // window
let foo4 = foo().call(obj) // window

3.隐式绑定无效

let fn = ()=>{
    console.log('箭头函数隐式绑定',this)
}
let fn2 = function(){
    console.log('普通函数隐式绑定',this)

}
let obj = {
    foo:fn,
    foo2:fn2
}
obj.foo() // windows
obj.foo2() // obj { foo: [Function: fn], foo2: [Function: fn2] }