一.什么是this
首先明确一点,JS中 “万物皆对象”。那么this就是对象的指南针,每一个this都应该指向一个对象,如果没有对象让它指向,那么它就指向undefined(未知)。
二.为什么要了解this指向
首先一点应该是基于我们对JavaScript这门语言的热爱与尊重,就像我们对自己的爱人,有必要去了解对方的生活习性一样,如果只是勉强在一起连对方的生日都不知道,很明显这是一种不尊重以及不负责任的行为。
其次,才是我们在日常工作中的应用。在很多情况下我们的小伙伴经常会根据自己的代码习惯以及经验来对this的指向进行判断,这常常会导致一个奇怪的后果:就是一旦代码运行得到的结果和我们预想的结果不相符的时候就容易陷入一个怪圈:“MD看了这么多遍,明明代码没有一点问题,怎么会结果不对呢?”,于是找人帮忙检查问题,最后发现是自己对this指向产生了惯性思维,根据所谓“正确的经验”来对不符合实际情况的代码进行问题判断,自然你的代码肯定是“绝对不存在任何问题”了啦,所以这就需要我们不能对固定的,常见的代码格式产生惯性思维,不但要知其然,还要知其所以然。
三.怎么样结合代码判断this指向
首先明确一个基本概念:
所有 JavaScript 全局对象、函数以及变量均自动成为 window 对象的成员。
全局变量是 window 对象的属性。
全局函数是 window 对象的方法。
故有:
1、全局函数的情况下this始终指向window的全局变量
// 首先定义全局变量 tom
var tom = 'hello,111' // 注意:若使用 let 定义则不属于全局变量
//定义普通函数boss
function boss() {
let tom='hello,222'
console.log(this.tom)
}
//定义含有箭头函数的函数kiss
function kiss() {
let tom = 'hello,333'
return () => {
return () => {
console.log(this.tom)
}
}
}
//输出结果
boss() //hello,111
kiss() //hello,111
//结论:全局函数中this默认指向window(全局变量),不受函数作用域影响
而HTML DOM 的 document 也是 window 对象的属性之一
故有:
2、DOM事件的情况下事件绑定里的 this 指向随着函数绑定环境而变,但默认是指向 window
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<button id="button" onclick="touchMe">点我</button>
<script type="text/javascript">
var tom = 'hello,world' // 定义全局变量tom
//1.第一种函数环境(全局函数)
function touchMe() {
//this默认指向window全局变量
console.log(this.tom)
}
//2.第二种函数环境(对象中的方法,对象obj本身存在tom属性)
let obj = {
tom: 'hello,obj',
touchMe() {
//touchMe函数this指向obj本身作用域
console.log(this.tom)
}
}
// this随函数绑定环境而变:
//绑定环境1:document.getElementById('button').onclick = touchMe() :输出 hello,world
//绑定环境2:document.getElementById('button').onclick = obj.touchMe() :输出 hello,obj
</script>
</body>
</html>
函数被构建的意义即是便于调用,而调用时this指向如下:
3、谁调用函数,this就指向谁
class One{
constructor(tom) {
this.tom = tom
}
getTom() {
console.log(this.tom)
}
}
//new两个新对象继承函数One的方法
const f1 = new Foo('Hello,world')
const f2 = new Foo('Hello,Tony')
f1.getTom() // 输出 Hello,world 证明this指向f1
f2.getTom() // 输出 Hello,Tony 证明this指向f2
let obj = {
tom: 'Hello,obj',
getTom() {
console.log(this.tom)
}
}
obj.getTom() // 输出 Hello,obj 证明this指向-对象obj,obj本身作用域中存在属性tom
结论:如“f1.getTom()”、“f2.getTom()”、“getTom.getTom()”中,谁"."了函数,而"."即调用,那么函数的this就指向谁。 口诀:"点的左边是对象,括号左边是函数",所以this指向点左边的对象。
但由于每一门语法的出现都必然存在不断改进的空间,容易出现不合理、不严谨之处,所以就需要注意严格模式"use strict"模式下的this指向:
4、严格模式下,this 指向 undefined
"use strict"
function getThis() {
console.log(this)
}
getThis() // 输出 undefined
另外,相信在各位学习this指向的过程中,最迷惑的应该是箭头函数与普通函数之间的区别了吧
在这里我简单描述他们最大的区别,也是最容易判断的区别:箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this。
结合setTimeout 里面的 this举例:
5、普通函数 this 默认指向 window,而箭头函数的 this 指向上一层环境
var tom = 'hello,world'
let obj = {
tom: 'hello,obj',
getTom() {
//此时setTimeout函数里面的 console.log(this.tom) 就相当于直接输出 console.log(this.tom)
setTimeout(function() {
console.log(this.tom)
})
}
}
obj.getTom() // 输出 hello,world ,证明函数存在自身默认的this指向,即this指向window全局
注意:当普通函数位于setTimeout()中时,则函数脱离obj对象独立存在于全局运行,并非是obj调用函数方法getTom,故而this不指向调用对象obj,指向全局变量
var tom = 'hello,world'
let obj = {
tom: 'hello,obj',
getTom() {
//此时setTimeout函数里面的 console.log(this.tom) 就相当于直接输出 console.log(this.tom)
setTimeout(() => {
console.log(this.tom)
})
}
}
obj.getTom() // 输出 hello,obj ,证明箭头函数无自身创建的this,需要从上一级函数getTom中继承,即局部对象obj的作用域,所以产生了对obj对象的绑定
最后,扩展一下改变this指向的三个方法apply / call / bind及使用如下:
let a = {
title: 'Hello,world',
getTitle() {
console.log(this.title)
}
}
let b = {
title: 'Hello,kitty'
}
a.getTitle() // 正常输出 Hello,world
a.getTitle.apply(b, ['参数1', '参数2']) // 将 this 环境指向 b ,输出 Hello,kitty
a.getTitle.call(b, '参数1', '参数2') // 将 this 环境指向 b ,输出 Hello,kitty
a.getTitle.bind(b)('参数1', '参数2') // 将 this 环境指向 b ,输出 Hello,kitty
// 注意:其中bind 不能多次指向,因为第一次绑定时环境就已经确定。
举例:a.getTitle.bind(b).bind(a)() // this 环境依然属于 b
闲的无聊,随便写写,偶有借鉴,彼此共勉。