this| 青训营笔记

108 阅读3分钟

这是我参与「第四届青训营 」笔记创作活动的第6天

一、This永远指向最后调用它的那个对象

image.png

打印:

image.png

解释:

1、a.fn() 约等于 window.a.fn() ,但最后调用fn的是a对象,所以fn中的this指向a。就算a中没有this.age这个属性,也不会继续向上一个对象/作用域中查找,因为name的查找已经限定在this中了,即this.name是在‘this’上下文中寻找name

2、let f = a.fn只是赋值,没有调用。fn() 约等于 window.fn() ,因为this永远指向最后调用它的那个对象,所以fn中的this指向window

在ES5中 a() 约等于window.a() ,严格模式下,全局对象是undefined

二:如何改变this指向:

demo

var name = 'windowsName'
var a = {
        name: 'cherry',
        fun1(){
            console.log(this.name)
        },
        fun2(){
            setTimeout(function() {
                    console.log(this)
                    this.fun1()
            },3000)
        }
}
a.fun2()

打印:

image.png

解释: 因为定时器内的回调函数在三秒后才执行,定时器setTimeout的执行环境和a对象的执行环境分开了,导致this指向了默认的window,window下没有fun1,所以报错了

 

解决:

方法一:箭头函数

箭头函数无this,继承自上层作用域的this。(普通函数/window才算作用域,对象不算作用域)

更改的部分:

fun2(){
    console.log(this)
    setTimeout(() => {
            console.log(this)
            this.fun1()
    },3000)
}

image.png

如图:第一行打印的是fun2的this,第二行是箭头函数内的this和调用this.fun1打印的this.name

解释: 箭头函数定义时的上层作用域是fun2函数,所以箭头函数内的this指向fun2的this,都指向a对象,所以this.name打印出a对象的name:‘cherry’

方法二:

在调用这个函数的对象使用_this = this(如果不使用ES6,这应该是最不会出错的方式了)

更改的部分:

fun2(){
    let _this = this
    console.log(this)
    setTimeout(function()  {
            console.log(_this)
            _this.fun1()
    },3000)
}

image.png

解释: 在fun2中用_this 保存了this,然后在定时器的回调中使用_this,指向的就是fun2中的this。

换种说法就是调用了上级作用域中的变量,于是与该作用域发生了关联,该作用域的变量保持活跃,于是可以在定时器的回调触发时,a作用域销毁时,还能调用上级作用域的变量。

方法三:使用call、apply、bind

更改的部分:

fun2(){
    setTimeout(function() {
            console.log(this)
            this.fun1()
    }.call(a),3000)
    //或apply(a)、bind(a)()
}

image.png

弊端: this指向确实改变了,但是定时器也失效了,回调立即发生。

  Fn.apply( thisArg [,argsArray ] )

(下面的‘=’代表等于,不是赋值)

1、在ES5非严格模式下,thisArg = null/undefined 时,this自动指向window全局对象;

2、thisArg = 原始值(数字、字符串、布尔值)时,this指向该原始值的自动包装对象

例如:apply(123),this指向Number自动包装对象

image.png

3、ThisArg = 对象,则this指向该对象

4、传参 [ArgsArray],包含多个参数的数组

区别:call 接收的传参是参数列表、bind接收的也是参数列表,但因为bind是创建一个新的函数,所以需要额外调用函数bind()()