javascript --this指向问题

162 阅读2分钟

this 的指向问题一直是一个难题,我看过很多有关this的文章,当时都大致都懂了,但是过一段时间后还是忘记。是因为记得不牢固的原因。

想要牢固的记住this指向问题,就自己来总结一篇“属于你自己的this指向问题”。

先来例子:

let myObject = {
  name: "phoebe",
  age: 22,
  aboutMe: function(){
    return "My name is "+this.name+", I am "+this.age + " years old"
  }
}
console.log(myObject.aboutMe());

此时,打印出来的是: My name is phoebe, I am 22 years old

let myObject2 = {
  name: "phoebe",
  age: 22,
  aboutMe: function(){
    return "My name is "+this.name+", I am "+this.age + " years old"
  },
  friends: {
    name: "fanfan",
    age: 24,
    aboutMe: function(){
      return "My friend's name is "+this.name+", he is  "+this.age + " years old"
    }
}
console.log(myObject2.aboutMe());
console.log(
myObject2.friends.aboutMe()) // ;

此时,打印出来的是: My name is phoebe, I am 22 years old

My friend's name is fanfan, he is 24 years old

这时我们可以总结: this关键字总是隐式的去引用离他最近的包含对象

或者是说永远指向最后调用他的那个对象

myObject2.aboutMe() // this指向的是myObject2
myObject2.friends.aboutMe()) // this指向的是myObject2里的friends对象 

再看一个例子:

let myObject3 = {
  firstName: "Harry",
  age: 20,
  aboutMe: function(){
    setTimeout(function(){
      console.log(this.firstName + " is " + this.age + " years old")
    },5000)
  }
}
myObject3.aboutMe();

此时,打印的结果便是undefined is undefined years old.

setTimeout函数是一个异步函数,他会在宏任务中的同步函数调用完后,再去调用。5秒后再去执行时,this所在的环境就是全局环境。指向的也是全局变量(window),而在全局环境中,fristname和age属性都是未定义的。所以会报undefined。

此时,要想正常运行我们需要怎样做呢?

那就是把this再重新指回来,重新指向myObject3这个对象。

方法:

1.箭头函数(最方便的)
2.call
3.apply
4.bind

箭头函数的使用,推荐去看阮大神的书籍。

call方法的使用:

let user1 = {
  name: "Tony",
  sayHi: function(){
    return this.name + " says Hi"
  }
}
let user2 = {
  name: "Martha",
  sayHi: function(){
    return this.name + " says Hi"
  }
}
console.log(user1.sayHi()); //Tony says Hi
console.log(user2.sayHi()); //Martha says Hi
    

两个对象都有sayHi方法,那我们可不可以减少代码只保留一个sayHi方法。

let user1 = {
  name: "Tony",
  sayHi: function(){
    return this.name + " says Hi"
  }
}
let user2 = {
  name: "Martha"
}
console.log(user1.sayHi());           //Tony says Hi
console.log(user1.sayHi.call(user2)); //Martha says Hi

可以达到相同的效果,如何我们想增加一些参数呢?

let user1 = {
  name: "Tony",
  greet: function(msg, friend){
    return this.name + " says " +msg+ " to "+friend
  }
}
let user2 = {
  name: "Martha"
}
console.log(user1.greet("Hi", "James"));
//Tony says Hi to James
console.log(user1.greet.call(user2, "Hello", "Habeeb"));
//Martha says Hello to Habeeb

call方法可以接收多个参数

apply方法的使用:

apply方法和 call方法是很像的,唯一不同的是 apply方法只需要一个参数,(如果需要多个参数时,就要用数组了) console.log(user1.greet.apply(user2, ["Hello", "Habeeb"]));

bind方法的使用:

返回一个他所调用的函数,但不会立即去执行他。
let myObject4 = {
  firstName: "Michael",
  age: 20,
  aboutMe: function(){
    setTimeout(function(){
      console.log(this.firstName + " is " + this.age + " years old")
    }.bind(myObject4),5000)
  }
}
myObject4.aboutMe(); //Michael is 20 years old