1.产生的问题
- 1.类中的this指向
- 2.方法中的this指向
- 3.匿名函数中的this指向
- 4.怎么解决this指向问题
2.样例及解决
1.类中的this指向
<script type="text/javascript">
class Person{
// 构造器
constructor(id, name){
console.log("类中构造器的this指向: ", this)
this.id = id
this.name = name
}
// 普通方法
eat(){
console.log("类中方法的this指向: ", this)
}
}
const p1 = new Person(1, 'lily')
p1.eat(); // 通过Person实例调用eat方法,其中this是Person的实例
console.log("===================")
const temp = p1.eat // 将Person类的特殊属性(方法)eat赋值给temp
console.log("方法的定义: ", temp) // 输出方法的定义
console.log("将Person类内部的eat方法赋值给temp变量,并调用:")
temp() // 直接调用temp方法,this是undefined
</script>
完整输出为如下:
- 1.最后一行
temp() // 直接调用temp方法,this是undefined
输出为什么是undefined,而不是Person实例或者window对象?- 原因:因为js在类中自动开启了严格模式(
"use strict";//禁止this关键字指向全局对象
),所以这里的this为undefined。
- 原因:因为js在类中自动开启了严格模式(
2.方法中的this指向
<script type="text/javascript">
function test1(){
console.log(this) // 方法中直接输出this指向window对象
}
function test2(){
"use strict";
console.log(this) // 加了严格模式,输出this指向为undefined
}
</script>
输出如下:
3.匿名函数中的this指向
<script type="text/javascript">
// 匿名函数测试1
const temp1 = {
id: 123,
temp1: function(){
console.log("外部this指向: ", this) // 输出Object对象(及js对象)
return function(){
console.log("内部this指向: ", this) // 输出window对象
}
}
}
console.log("匿名函数定义: ", temp1.temp1()) // 输出外部this并同时输出匿名函数定义
temp1.temp1()() // 内部的匿名函数调用
console.log("================")
// 匿名函数测试2
const btn = document.getElementById('btn').addEventListener('click', ()=>{
console.log("监听事件的匿名函数中this指向: ", this)
btnInner();
btnInner1();
(function(){
console.log("匿名函数中的匿名函数中的this指向: ", this)
})()
})
function btnInner(){
console.log("匿名函数调用非严格模式中的this指向: ", this)
}
function btnInner1(){
"use strict";
console.log("匿名函数调用严格模式中的this指向: ", this)
}
</script>
输出如下:
4.怎么解决this指向问题
- 1.全局定义一个变量that,将this赋值给变量that,然后使用(视开发情况而使用)
- 2.使用call,apply,bind方法解决this指向
1.先说call
call(this指向, 函数参数, 函数参数...)
, this指向就是需要把哪个对象实例传进去,函数参数就是调用call方法的那个函数的参数(有点绕口,看例子)。这个例子中myIntroduce有俩个参数,name没有使用this访问,所以可以直接传入参数输出出来,而age使用this去访问的(本来this应该指向objCall,但是此时this却为undefined,这并不是我们所期望的),所以使用call改变this的指向由undefined到objCall就可以访问到age。注意:我这里专门传入第二个参数19,结果却输出13,原因很简单(没有this.age=age),这里的age并没有set到objCall的原型链上。
<script type="text/javascript">
const objCall = {
age: 13,
myIntroduce: function(name, age){
console.log("我是:", name, ",性别:", this.age)
}
}
const callIntroduce = objCall.myIntroduce
callIntroduce('约翰', 19) // this指向window对象, 但是我们期望的是this指向objCall对象
// 使用call
callIntroduce.call(objCall, '约翰', 19)
</script>
2.再说apply
apply(this指向, [函数参数, 函数参数...])
,没错,apply和call的区别就是apply的第二个参数是一个数组。(例子如下),从下面这个例子可以看出方法1.apply(this指向另一个对象noObjApply ,[参数1, 参数2]),而在noObjApply中由一个与方法1同名的方法2,最后的结果是方法1可以使用noObjApply中的属性,而不会直接换到方法2去
<script type="text/javascript">
const objCall = {
age: 13,
myIntroduce: function(name, age){
console.log("我是:", name, ",性别:", this.age)
}
}
const noObjApply = {
age: 20,
myIntroduce: function(name, age){
console.log("我是noObj名字为:", name, ",性别:", this.age)
}
}
const callIntroduce = objCall.myIntroduce
callIntroduce('约翰', 19) // this指向window对象, 但是我们期望的是this指向objCall对象
// 使用apply
callIntroduce.apply(objCall, ['约翰', 19])
callIntroduce.apply(noObjApply, ['约翰', 19])
</script>
输出如下:
3.最后说bind
bind(this指向, 参数1, 参数2...)
,这个例子和前俩个差不多,有一点就是bind方式改变this指向返回一个新的函数,需要自己去调用这个函数。
<script type="text/javascript">
const objCall = {
age: 13,
myIntroduce: function(name, sex, age){
console.log("我是:", name, ",性别:", sex, ",年龄:", this.age)
}
}
const noObjApply = {
age: 20,
myIntroduce: function(name, sex, age){
console.log("我是noObj名字为:", name, ",性别:", sex, ",年龄:", this.age)
}
}
const callIntroduce = objCall.myIntroduce
callIntroduce('约翰', '男', 19) // this指向window对象, 但是我们期望的是this指向objCall对象
const changeThisP = callIntroduce.bind(objCall, '约翰', '男', 19) // bind返回一个函数,不会立即执行
changeThisP()
const changeThisP1 = callIntroduce.bind(noObjApply, '约翰', '男', 19) // bind返回一个函数,不会立即执行
changeThisP1()
const changeThisP2 = callIntroduce.bind(objCall, ['约翰', '男', 19]) // bind返回一个函数,不会立即执行
changeThisP2()
const changeThisP3 = callIntroduce.bind(noObjApply, ['约翰', '男', 19]) // bind返回一个函数,不会立即执行
changeThisP3()
</script>
输出如下:
3.问题总结
- 1.call和apply修改this指向都是调用的时候执行,唯一的不同点是传参问题,call的第二个参数以后直接传递参数,apply的第二个参数却是一个数组,调用方式都是及用及调。
- 2.bind与call和apply的不同
- 1.参数问题,bind的第二个参数如果为数组,需要改变this指向的方法内部使用参数的时候需要下标访问,如果第二个参数不是数组,而是多个参数,参数传递方式和call的一样。
- 2.调用问题,call和apply都是直接调用的,而bind则是返回一个函数,需要的时候再去调用。
- 3.附加: 还有一个箭头函数
()=>{}
,箭头函数中没有自己的this,如果在箭头函数中使用了this这个关键字(使用不报错),箭头函数就会找其外侧函数的this作为箭头函数的this去使用。