共计 5155 个字符,预计需要花费 13 分钟才能阅读完成。
深刻了解 JavaScript
的this
指针
hello everybody,我又来了。上一篇文章中,咱们解说了 JavaScript
的原型,有趣味的同学能够通过传送门进入查看,明天咱们来聊聊JavaScript
中的this
。一起探索下这个比拟难了解的问题。话不多说,让咱们进入正题吧????????????
1.this
指针具体是什么
首先开始第一个问题,想要理解this
,那就得先晓得它是什么货色。先来看一个例子。
this is a little dog. ---> 这是一只小狗
这句话能够让咱们很分明的明确 this
所指的是一只小狗。
灵光一闪,咦,如同明确了点儿什么。
不急,接着来看,
this.name = "二狗子"
通过这个句子,咱们能很快的晓得,这只小狗的名字叫 二狗子
。当初设想一下,将你的手指指向其余的小狗、小猫……
this is a cat.
this.name = "球球" // 这是一个猫,它叫球球
this is a other little dog.
this.name = "狗剩子" // 这是另一只小狗,他的名字叫 狗剩子
// 作者心田是解体的,身为一个英语渣渣,这几句英语解释,几乎要了老命。
看到这里的你是不是会感觉恍然大悟,咱们的手指指向谁,this
代表的就是谁。
哇哦,感觉关上了新世界的大门,this is so easy
。
重点来了!!!
this
到底是什么呢?
它是一个代指,咱们常常说在 JavaScript
中万物皆对象,那么,this
就是代指这一个个对象,最重要的是看 你(this
)的手指指向谁。
是不是须要坚固一下了?
this is lucy.
this is lilei.
this is liming.
this is me.
this is my brother.
this is a people.
…………………………
抛开那些烦人的英文吧,代码才是我的谋求……
2.this
指针的具体指向
想晓得 this
具体代指的是哪个对象,最要害的还是要弄懂它的指向。
2.1 无闭包的函数
闭包的内容会在后续章节中解说,敬请期待。
知识点 1:
个别状况下,this
代指的是调用它的那个对象(闭包和箭头函数除外,稍后解说)
怎么了解这句话呢?小栗子上场
<!doctype html>
<html lang="en"><head><meta charset="UTF-8"></head><body></body></html>
<script>
function getName () {console.log(this === window) // true
}
// 调用
getName()
</script>
这里定义了一个 getName
函数。调用的时候并没有指定是谁调用,那,它的 this 指向到了哪儿呢?
知识点 2:
全局的办法和属性都是 window
对象的办法和属性,也就是说,这里的调用其实是 window.getName()
,是 window
调用的,也就是它的 this
指向是window
没了解?好吧,再看一个栗子
<!doctype html>
<html lang="en"><head><meta charset="UTF-8"></head><body></body></html>
<script>
let obj = {getName: getName}
function getName () {console.log(this === window) // false
console.log(this === obj) // true
}
obj.getName()
</script>
定义一个对象,将 getName
当做这个对象的办法,最初通过 obj.getName()
来执行,你会惊喜的发现,this 曾经指向了obj
。起因就是:调用函数的对象是obj
2.2 有闭包的函数
家喻户晓,计时器也是闭包的一种,为了不让大家对代码有不了解的中央,这里咱们应用计时器实现闭包。
还是相熟的配方,还是相熟的滋味,还是相熟的栗子。
<!doctype html>
<html lang="en"><head><meta charset="UTF-8"></head><body></body></html>
<script>
let obj = {getName: getName}
function getName () {setTimeout(function xiaosan() {console.log(this === obj) // false
}, 0)
}
obj.getName()
</script>
纳尼!!不是 obj
调用的吗?不是 this
会指向 obj
吗?到底是谁,是谁让本来忠贞的恋情呈现了裂缝?
别急别急,咱们来剖析一下,obj
调动了 getName
函数。然而计时器中的 xiaosan
函数并不是由 obj
调用的,所以 this
必定不会指向 obj
。那么,xiaosan
的this
指向了谁呢?咱们将 setTimeout
改写为上面这种写法
setTimeout(function xiaosan() {console.log(this) // window
}, 0)
哦~ 原来 window
就是那个第三者。终于抓到你了。
见证奇观的时刻,让咱们为错位的恋情复位吧!
<!doctype html>
<html lang="en"><head><meta charset="UTF-8"></head><body></body></html>
<script>
let obj = {getName: getName}
function getName () {setTimeout(function xiaosan() {console.log(this === obj) // true
}.bind(this), 0)
}
obj.getName()
</script>
长舒一口气,终于圆满了。
知识点 3:
闭包中的 this
指向为 window
。能够应用 箭头函数、call、apply、bind
等手法扭转 this
指向。
2.3 箭头函数
首先介绍下箭头函数。官网文档 说到,箭头函数有以下几个个性:
- 没有独自的
this
- 不能通过
call、apply、bind
办法扭转this
指向(因为没有????) - 不绑定
arguments
- 不能应用
new
操作符(因为没有this
????) - 没有
prototype
(具体介绍请看我上一篇文章) -
不能作为函数生成器,不能在箭头函数中应用
yield
- 除非是箭头函数内嵌套了一个一般函数,将
yield
用在一般函数中
- 除非是箭头函数内嵌套了一个一般函数,将
看到这里忽然发现,箭头函数切实是太惨了,这么多不能干的。既然这么多限度,那还用箭头函数干什么?
益处很多,这里咱们只介绍与本节课相干的,箭头函数能够“主动绑定”this
举个栗子看下:
<!doctype html>
<html lang="en"><head><meta charset="UTF-8"></head><body></body></html>
<script>
let obj = {getName: () => {console.log(this === obj) // false
},
getAge: function () {console.log(this === obj) // true
}
}
obj.getName()
obj.getAge()
</script>
同样的两个办法调用,第一个返回的居然是 false
。这是为什么?明明我就是应用obj
调用的。别急,咱们来打印下this
,看它指向了哪儿。
getName: () => {console.log(this) // window
},
知识点 4:
箭头函数不会创立本人的this
,只会从作用域链的上一层继承this
这里的作用域上一层为 window
对象,所以,this
指向了window
。
咱们再来看看闭包中的箭头函数
<!doctype html>
<html lang="en"><head><meta charset="UTF-8"></head><body></body></html>
<script>
let obj = {getName: getName}
function getName () {setTimeout(() => {console.log(this === obj) // true
}, 0)
}
obj.getName()
</script>
方才咱们这个打印是 false
,当初咱们把xiaosan
函数改成箭头函数,惊喜的发现,this
指向了 obj
。是因为getName
函数的上一层 this
是obj
,继承了过去。
3. 批改 this 指针的指向
手动扭转 this
指针,能够帮忙咱们更明确的晓得本人要执行的办法。扭转指针的办法有三种。
- call
- apply
- bind
3.1 call、apply
这两兄弟性情相近,那就放到一块儿来介绍吧。
应用 call()
或apply()
裁减作用域最大的益处就是, 对象不须要与办法有任何耦合关系,
它们的作用就是在函数的体内设置 this
的值。也就是咱们说的扭转 this
的指向。怎么扭转的呢?
???? 小栗子又要出场了。
call 的小栗子
<!doctype html>
<html lang="en"><head><meta charset="UTF-8"></head><body></body></html>
<script>
let obj = {
name: '一个对象',
getName: function (a,b,c) {globalGetName.call(this,a,b,c)
}
}
function globalGetName (a,b,c) {console.log(this.name,a,b,c) // 一个对象 xiaosan xiaosi xiaowu
}
obj.getName("xiaosan", "xiaosi", "xiaowu")
</script>
知识点 5:
call
的特点
- 将
this
绑定到以后环境中, - 参数一一传入。
apply 的小栗子
<!doctype html>
<html lang="en"><head><meta charset="UTF-8"></head><body></body></html>
<script>
let obj = {
name: '一个对象',
getName: function (a,b,c) {globalGetName.apply(this,[a,b,c])
}
}
function globalGetName (a,b,c) {console.log(this.name,a,b,c) // 一个对象 xiaosan xiaosi xiaowu
}
obj.getName("xiaosan", "xiaosi", "xiaowu")
</script>
知识点 6:
apply
的特点
- 将
this
绑定到以后环境中, - 传入一个参数列表,如
[a,b,c]
。
3.2 bind
在调用 bind
办法之后,会将 bind
中传入的第一个参数作为 this
绑定到以后函数上。之后的参数会一一传入到函数中。
<!doctype html>
<html lang="en"><head><meta charset="UTF-8"></head><body></body></html>
<script>
let obj = {name: '一个对象'}
function getName (a) {console.log(this.name, a)
}
getName.bind(obj, '111')() // 一个对象 111
</script>
能够看到,咱们通过 getName.bind
将obj
传入,作为 getName
的this
。那么在执行 getName
的时候,this
代指的就是 obj
对象。那么 this.name === obj.name
。将第二个参数通过入参的形式传入到getName
上。
知识点 7:
bind
的特点
- 不会主动执行,通过
bind
绑定this
后,须要手动执行函数。如getName.bind(obj, '111')()
- 传参形式和
call
雷同
首先,祝贺能看到这里的你,阐明我的文章也不是这么让人腻烦。接下来我会通过一个面试题来具体解说 this
指针的变换。筹备好了吗?Let’s go……
4. 面试题阐明
var foo = 'fooBar'
var obj = {
foo: "bar",
func: function () {
var self = this;
console.log(this.foo); // 1
console.log(self.foo); // 2
(function () {console.log(this.foo); // 3
console.log(self.foo); // 4
}());
}
};
obj.func();
输入:
- bar
- bar
- fooBar
- Bar
解析:
- 当执行
obj.func
的时候,this
指向了obj
。此时1 和 2
都能失常的方位obj.foo
3
因为在一个闭包中,this
指向了window
,所以会找到全局的foo =‘fooBar’
4
因为缓存了this
,所以self
代指的还是obj
这个对象,self.foo === obj.foo
。