引言
为什么要用this,我们举个栗子:
北京古称燕京、北平,北京是中华人民共和国的首都,北京有长城、故宫等名胜古迹,北京是中国的政治经济中心。
这句话讲述主语是不是有点啰嗦?
替换成成this后的句子:
北京古称燕京、北平,这里是中华人民共和国的首都,这里有长城、故宫等名胜古迹,这里是中国的政治经济中心。
我们看看下面两组代码块:
var person = {
name:'小明',
bobby:'打篮球',
info:function(){
console.log(`大家好,我是${person.name},我喜欢${person.bobby}`)
}
}
person.info()
var person = {
name:'小明',
bobby:'打篮球',
info:function(){
console.log(`大家好,我是${this.name},我喜欢${this.bobby}`)
}
}
person.info()
如果对象名很长的话,就会造成代码的冗余,所以使用this替换后的代码,变得简洁干净也便于阅读。
概念
大家最关心的应该是this的指向问题了,先给大家罗列几种常见的指代对象的方法:
this指向 | |
调用主体 | 指向 |
普通函数 | window |
对象 | 该对象 |
定时器、延时器 | window |
call、apply | 该对象 |
事件绑定 | 绑定的DOM元素 |
构造函数 | 该实例 |
笔者用一种有趣的方法教大家记忆一下:
普通函数
有人问小明,有没有女朋友,小明说有,但是没能说出女朋友是谁,所以就默认全世界中的某个女生是他女朋友,所以this指向全局,也就是window
function fn(){
console.log(this) //window
}
fn()
对象
过了一段时间,又有人问小明有没有女朋友,这次小明不但说有,而且还能准确地说出女朋友的信息。当我们调用对象的方法时,就会出现隐式绑定,因为info方法是在girl里面的,所以this指向girl对象。
var girl = {
name:"小红",
age:"18",
hobby:"写代码",
info:function fn(){
console.log(`我是小明的女朋友${this.name},今年${this.age}岁,喜欢${this.hobby}`)
console.log(this) //girl
}
}
girl.info()
call、apply
此时因为一些矛盾,小明跟小红分手了,然后小明换了个新女友小白,并对公布了她的身份。apply和call的用法相似,都是拥有改变this指向的魔法,后面会有介绍。所以this指向new_girl对象
var girlName = {
name:"小红",
info:function fn(){
console.log(`我是小明的女朋友${this.name}`)
console.log(this) //girl1
}
}
var new_girl = {
name:"小白"
}
girlName.info.call(new_girl)
构造函数
所谓浪子回头金不换,小明最终还是幡然醒悟,回到了小红的身边,并且扯证结婚。构造函数就像契约一样,把两人紧紧的捆绑在一起,所以this最终指向构造函数的实例xiaohong。
function Lover(name){
this.name = name
this.say = function(){
console.log("我跟"+name+"结婚啦!")
console.log(this) //xiaohong
}
}
var name = "小白"
var xiaohong = new Lover("小红")
xiaohong.say()
还有定时器和事件绑定的指向的故事就不讲了,总结来说this的指向浓缩成一句话:
谁调用就指向谁
注意:箭头函数没有自己的this,它的this指向函数上层作用域的this
var name = "a"
var obj = {
name:"b",
fn:()=>{
console.log(this.name)
}
}
obj.fn()//a
了解完基础概念以后整点题目巩固一下(答案在所有题目的后面)
第一题:
function a(){
console.log(this)
function b(){
console.log(this)
function c(){
console.log(this)
}
c()
}
b()
}
a()
第二题:
var name = "a"
function fn1(){
console.log(this.name)
}
var A = {
name:"b",
fn2:function(){
console.log(this.name)
},
B:{
name:"c",
fn3:function(){
console.log(this.name)
}
},
fn4:fn1
}
A.fn2()
A.B.fn3()
A.fn4()
第三题:
var name = "a"
function fn1(){
var name ="b"
console.log(this.name)
}
function fn2(x){
return x()
}
var T = {
name:"c",
fn3:function(){
console.log(this.name)
},
fn4:function(){
return function(){
console.log(this.name)
};
}
}
var B1 = T.fn3
T.fn1 = fn1
var B2 = T.fn4()
fn1()
B1()
T.fn1()
fn2(T.fn3)
B2()
答案:
1.window window window
2.b c b
3.a a c a a
call、apply和bind
上述三个讲明白一个,其他那两个大家也跟着学会了
前面已经讲到,call、apply有一个两个强大的功能:
1.可以调用函数
2.改变this的指向
先那call说事,举个例子给大家:
小猫和小狗出去玩,小狗带了充电宝且手机还有80%电,小猫的手机只有10%的电量,但是小猫没有充电宝该怎么办呢?当然是问小狗借啦!
由于cat对象没用charge方法,所以需要通过call方法去改变this的指向,指向cat本身,所以就能成功调用charge方法。
call(对象,值)//call函数第一个参数为你需要this指向的那个对象,第二个参数是你要传的参数
let dog = {
phone:80,
charge:function(level){
this.phone= level
}
}
let cat = {
phone:10
}
dog.charge.call(cat,100)
console.log(cat.phone)
那apply这个方法怎么用呢?其实很简单,跟call差不多,只不过传递的参数需要以数组的形式传递
let dog = {
phone:80,
charge:function(level1,level2){
this.phone1 = level1
this.phone2 = level2
}
}
let cat = {
phone1:10,
phone2:5
}
dog.charge.apply(cat,[100,90])
console.log(cat.phone1,cat.phone2)
dog.charge.apply(cat,[100,90]) 等价于 dog.charge.call(cat,100,90)
bind在这里就特殊一点,因为他不像call和apply一样,可以调用函数,bind方法会把函数作为返回值返回出来,然后需要一个参数去接收返回值再调用。
let fn = dog.charge.bind(cat,100,90)
fn()
类比记忆:
call和bind接收参数的方法一致
call和apply都可以调用函数
今天你学废了吗?