0


【JS】Javascript中的this到底是什么

    JavaScript中的this是一个对于新手来说特别吓人并且不友好的概念。对于一个前端萌新来说,这个概念既模糊,又看不到它存在的意义。本文将深度解析JavaScript中的this,并且逐一分析他在每个常见场景中的意义和指向。

基本篇: 什么是this?

    简而言之, this代表存在于函数的一个对象,而这个对象就是调用这个函数的那个对象。

    假设我们有一个对象叫做counter, 其中有两个函数,分别是increment和log。 当你调用counter中的函数的时候,你就可以获得函数中的this对象。

    首先我们先试用log函数,来看看这里面的this到底是什么。
let counter = {
            count: 0,
            increment() {
                return this.count
            },
            log() {
                console.log(this)
            }
        }

counter.log()//{count: 0, increment: ƒ, log: ƒ}

counter.log()的结果显示,log函数的this就是counter这个object,也就是log函数的调用者。

counter.increment()//0
       以此类推,我们可以得出increment的结果是0,也就是对象中count的值。

     但现在你也许会问,那如果我们在全局环境下不通过一个对象,直接调用函数,他的this回事什么呢?请看这个例子:
function printThis() {
    console.log(this)
}
printThis()//Window

在全局环境下直接调用函数,this的指向将会是Window对象自身。但这并不是一个固定的情况。如果你在函数内或者他的全局环境内写明了 "use strict" 关键词,那么这个函数将在严格模式下执行,而此时,在window环境下直接调用函数将不再返回Window, 而是undefined

function printThis() {
    "use strict"
    console.log(this)
}
printThis()//undefined

你可能会问,什么是严格模式?简而言之,严格模式的存在是为了写更严谨的代码,从而减少bug,并为未来JS的版本做铺垫。 在我们的案例中,严格模式会改变this的指向,将无调用者的函数的this从Window变成undefined。

    但是尽管在严格模式下的无调用者的函数中的this不会默认指向Window,如果你直接在全局作用域打印this,this仍然会指向Window。
"use strict"
let x = this
console.log(x)//Window
    接着我们之前的话题,我们接着使用之前的counter对象,我们再声明一个新的函数 newLog, 而这个newLog的值就是counter中的log方法:
let counter = {
    count: 0,
    increment() {
        console.log(this.count)
    },
    log() {
        console.log(this)
    }
}

let newLog = counter.log

当我们调用newLog的时候,你也许会认为,调用newLog的结果也会是counter对象,但是:

counter.log()//Window
newLog()//undefined

newLog的this并不是counter对象,而是Window,因为counter并没有亲自调用newLog。即使newLog和counter中的log的内容完全一样,他们指向的this并不一定相同。

    如果你在一个html标签中定义this,那么这个this将会指向这个html标签,你也可以通过getAttribute来获得这个标签上的属性
<div class="box" id="box" name="haha">i am a box</div>

<script> 
    const box = document.querySelector('.box')

    box.addEventListener('click', function() {
        console.log(this) // <div class="box" id="box" name="haha">i am a box</div>
        console.log(this.getAttribute('name')) // haha
    })
</script>

以上就是JavaScript中的this的基本理解和用法,接下来的内容将一定程度利用其他有可能难以理解的知识。


构造函数中的this

    在构造函数中,this指向的是构造函数的new出来的实例对象。在接下来的例子中,我们有一个构造函数Person,我们在这个构造函数的原型上添加一个函数sayHi,然后我们我们构造一个他的实例对象newPerson,并调用sayHi来看看他的this指向哪里
function Person(name, age) {
    this.name = name,
    this.age = age
}

Person.prototype.sayHi = function() {
    console.log(this, this.name, this.age)
}

let newPerson = new Person('tom', 30)
newPerson.sayHi()// {name: 'tom', age: 30} 'tom' 30
    正如我们刚才所讲,这里newPerson的this并不指向刚刚的构造函数Person,而是实例对象本身。同理,class中的this也将指向实例对象
class User {
    constructor(name){
        this.name = name
    }

    sayName() {
        console.log(this.name)
    }
}

let user = new User('playerone')
user.sayName()//playerone

Call, Apply, 和 Bind改变this

    如果你接触JS的时间不长,你也许对这三个函数很陌生。但他们的基本概念和用法其实特别简单,并且他们三个本质上做同一件事并且用法类似,就是修改一个函数的this指向。他们的语法也不难,都是函数自带的原型方法:

    function.apply(thisArg, argsArray)

    function.call(thisArg, arg1, /* …, */ argN)

    function.bind(thisArg,[, arg1[, arg2[, ...]]])   ==>(这种以[]嵌套的写法经常出现,代表该参数是选填的)

    他们三个第一个参数都是一个对象,而这个对象将变成这个函数的新的this。后面跟着的是该函数的参数。唯一的区别是他们传参数的方式;apply会把所有函数的参数作为一个数组传入。call会以逗号隔开,分别作为多个参数传入。bind的情况稍微特殊一些,。最后,call和apply的使用会直接调用该函数,而bind不会。
let obj = {
    username: 'lucy',
    age: 30,
    hobby: 'running'
}

function logAge() {
    console.log(`${this.username} is ${this.age} years old`)
}

function logInfo(a, b) {
    console.log(a, b)
}

function addArgs(a, b, c) {
    console.log("a: " + a)
    console.log("b: " + b)
    console.log("c: " + c)
    return a + b + c
}

// logAge() // undefined is undefined years old

// logAge.call(obj) // lucy is 30 years old

// logAge.apply(obj) // lucy is 30 years old

// logAge.bind(obj)() // lucy is 30 years old
// 后面的小括号()用来调用函数,因为bind并不会立刻调用函数

logInfo.apply(null, [20, 30]) // 20 30

logInfo.call(null, 20, 30) // 20 30

let newAddArgs = addArgs.bind(null, 30, 'haha', [1,2,3])

newAddArgs() // a: 30, b: 'haha', c: [1,2,3]

//因为bind中的参数会为该函数设置预设参数,即使你自己再传参也不会有任何效果
newAddArgs('bb', 'cc') // a: 30, b: 'haha', c: [1,2,3] 

箭头函数继承this

    箭头函数是一个ES6的主要特性之一,虽然我们在代码中经常遇到他,但你知道很多时候他存在的真正意义并不是为了更加简洁的语法,而是因为他独特的this指向。箭头函数自身并没有this对象。我们在开头讲过,函数中this一般指向他的调用者,而箭头函数的this, 它只会从自己作用域上一层继承this。也就是说,它的this和定义自己的“主人”的this相同。如果上一侧也没有,就继续往上一层去找,直到找到Window。call和apply也对箭头函数无效,声明严格模式也没有影响。

    在下面这段代码中,箭头函数的this和普通函数的指向并不相同
let obj = {
    name: 'kevin',
    printA: function () {
        console.log(this)
    },
    printB: () => {
        console.log(this)
    }
}

obj.printA()//{name: 'kevin', printA: ƒ, printB: ƒ}
obj.printB()//Window

在第二种情况中,箭头函数定义在另一个函数内,那么他将继承定义他的这个函数的this,也就是obj2这个对象

let obj2 = {
    name: 'john',
    printThis() {
        console.log(this) 
    },
    parentFunc() {
        const arrowPrint = () => [
            console.log(this) 
        ]
        printThis() // Window
        arrowPrint() // {name: 'john', printThis: ƒ, parentFunc: ƒ}
        console.log(this) // {name: 'john', printThis: ƒ, parentFunc: ƒ}
    }
}

obj2.parentFunc()
     第三种情况,如果我们分别把两个匿名的普通函数和箭头函数绑定给两个按钮,并且在全局作用域下定义另外一个箭头函数并在回调函数中调用它。那么, 不论如何,全局下的箭头函数的this都会指向window,因为箭头函数是在Window下定义的:定义它的对象。并且,你可以发现,普通函数如果直接作为元素的回调函数,那么他的this会指向该元素。
    <button class="btn1">1</button>
    <button class="btn2">2</button>
 
    <script>
        const btn1 = document.querySelector('.btn1')
        const btn2 = document.querySelector('.btn2')
        const btn3 = document.querySelector('.btn3')

        const printThis = () => {
            console.log(this)
        }

        function logThis() {
            console.log(this)
        }
        
        btn1.addEventListener('click', function () {
            console.log(this) // <button class="btn1">1</button>
            logThis() // Window
        })

        btn2.addEventListener('click', () => {
            console.log(this) // Window
            printThis() // Window
            logThis() // Window
        })

        btn1.addEventListener('click', printThis) // Window

        btn2.addEventListener('click', logThis) // <button class="btn1">1</button>
    </script>
    但是,当你在普通函数中加入严格模式,那么他的this指向将在匿名回调中变成undefined,正如我们之前所说,普通函数的this指向他的调用者。
        function logThis() {
            "use strict"
            console.log(this)
        }
        
        btn1.addEventListener('click', function () {
            logThis() // undefined
        })
        
        btn2.addEventListener('click', logThis) // <button class="btn1">1</button>
    以上就是JavaScript中的this在各种常见情况下的行为表现。下面这段W3Schools的话可以比较好地总结我们所讲的内容:

In an object method,

this

refers to the object. 在对象方式中,this指向该对象Alone,

this

refers to the global object. 自己被调用的话,指向全局对象In a function,

this

refers to the global object. 在函数中,this也指向全局对象In a function, in strict mode,

this

is

undefined

. 在函数中的严格模式下,指向undefinedIn an event,

this

refers to the element that received the event. 在事件中,指向触发事件的元素
Methods like

call()

,

apply()

, and

bind()

can refer

this

to any object.

call(), apply(), bind()可以把this重定向为任何对象


本文转载自: https://blog.csdn.net/a1611107035/article/details/127537376
版权归原作者 kevozzy 所有, 如有侵权,请联系我们删除。

“【JS】Javascript中的this到底是什么”的评论:

还没有评论