总结js中this的指向问题

121 阅读2分钟

有四种调用函数的方式,弄清楚这四种调用方式,this的指向问题就好解决了。

情况1 函数调用

函数调用的this指向window

function foo() {
  console.log(this.a)
}
var a = 1
foo()//1
复杂一点的情况

这种也属于函数调用,this指向window

var a=20
var foo = {
  a:15,
  b:function(){
    console.log(this.a);
  } 
}
var f1 = foo.b
f1()//20

情况2 方法调用

方法调用的this: 谁调用函数,谁就是this

var a=20
var foo = {
  a:15,
  b:function(){
    console.log(this.a)
  }
}
foo.b()//15
复杂一点的情况

say函数被当做foo.b()这种形式调用,函数内部的this指向调用者

var a=20
function say(){
  console.log(this.a)
}
var foo = {
  a:15,
  b:say
}
foo.b()//15

情况3 构造函数

这种调用函数的方法,this指向构造函数的实例

function foo(name) {
  this.name = name
}
let b = new foo('jack')//b中有属性name
复杂一点的情况

涉及到this可以访问什么?

function foo(){
  var _say = foo.prototype.say
  return new _say()
 }
foo.prototype = {
  constructor:foo,
  name:'jack',
  say:function(){
    console.log(this.name)
  }
 }
 foo()//undefined

从打印的情况看,foo访问不到name,这是为什么?

this可以访问实例say本身的属性,也可以访问say.prototype,并不可以访问foo.prototype,所以打印undefined

要想访问怎么办?修改say.prototype为foo.prototype

function foo(){
  var _say = foo.prototype.say
  return new _say()
 }
foo.prototype = {
  constructor:foo,
  name:'jack',
  say:function(){
    console.log(this.name)
  }
 }
 //添加这行代码,改变say的默认原型
 foo.prototype.say.prototype = foo.prototype
 foo()//jack

上下文调用(call,bind,apply)

对于上下文调用的认识
  • 三者都能改变函数内部this的指向
  • call和apply会执行函数,bind不会执行函数,而是返回新函数(返回的新函数只改变了this的指向)
  • call传递参数列表,apply传递数组
  • 如果不传递第一个参数,this默认是window
先看apply和call

call和apply打印结果一样

function f1(){
        console.log(this);
    }
    f1.call([1,3,5])
    f1.call({age:20,height:1000})
    f1.call(1)      
    f1.call("abc")
    f1.call(true);
    f1.call(null)
    f1.call(undefined);
    f1.call()
    console.log('=====')
    f1.apply([1,3,5])
    f1.apply({age:20,height:1000})
    f1.apply(1)      
    f1.apply("abc")
    f1.apply(true);
    f1.apply(null)
    f1.apply(undefined);
    f1.apply()
  • 总结:call、apply方法的第一个参数:

    • 1、如果是一个对象类型,那么函数内部的this指向该对象
    • 2、如果是undefined、null,那么函数内部的this指向window
    • 3、如果是数字,this对应的Number构造函数的实例
    • 4、如果是字符串,this:String构造函数的实例
    • 5、如果是布尔值,this:Boolean构造函数的实例
    再看bind

    返回一个新函数,新函数的逻辑和之前一样,只是改变了this的指向

    let name = 'window'
    function foo(){
      console.log(this.name)//打印foo
      }
    let newFoo = foo.bind({'name':'foo'})
    newFoo()
    

    其他情况

    箭头函数

    • 首先箭头函数其实是没有 this 的,箭头函数中的 this 只取决包裹箭头函数的第一个普通函数的 this

    function a() {
    return () => {
      return () => {
        console.log(this)
      }
    }
    

} console.log(a()()()) //window


##### 其他复杂一点的情况
> 通过上面的方法就能判断了

```js
var foo = {
  say:()=>{
    setTimeout(()=>{
      console.log(this)
   },100)
  },
  sing:function(){
    setTimeout(()=>{
      console.log(this)
    },100)
  },
  //es6写法
  eat(){
    setTimeout(()=>{
      console.log(this)
    },100)
  }
 }
 foo.say()//window
 foo.sing()//foo
 foo.eat()//foo

总结

通过网上找的一张图能够找到this(侵删),非常方便