浅谈js的this指向问题

JavaScript里函数的this指向,是一个挺难弄清楚的问题。网上大佬们的总结是:
哪个对象调用函数,函数里面的this就默认指向哪个对象。(注意this指向的只能是对象)
我个人把这句话理解成:看它函数名后加小括号时它的前缀是什么。
为什么说“默认指向”?因为JavaScript给我们提供了几种可以改变函数this指向的方法bindcallapply
接下来我们分别举几种常见情况来帮助理解this的默认指向。

一、全局作用域的函数中,this默认指向全局对象window。

如下,函数fn加小括号时它没有前缀,这时对它的调用fn()其实相当于window.fn(),所以this指向的是window。

function fn(){
    console.log(this);//window
}
fn();

但是这种情况在严格模式时有些不同
严格模式下,一般函数调用(不是对象的方法调用,也不使用apply/call/bind等修改this),this指向undefined,而不是全局对象。这是严格模式所规定的。

function fo(){
    'use strict'
    console.log(this);//undefined
}
fo();

二、对象里的函数,this指向该对象。

如下,函数fn的属于对象obj的属性:

var obj = {
    a:1,
    fn:function(){
        console.log(this);
    }
}
obj.fn();//obj

可以看到,调用fn时它的前缀是obj,所以它的this指向对象obj。

我们来看一种复杂的:
先声明一个全局作用域下的方法fn,它的功能是输出自己的this,在声明对象obj1和obj2,obj1里声明一个属性fn1,其功能与fn相同,obj2里也声明的属性fn2,除了和fn相同的功能外,fn2还调用了fn和fn1。

function fn(){
    console.log(this);
}

var obj1 = {
    a: 1,
    fn1: function () {
        console.log(this);
    }
}
var obj2 = {
    a: 2,
    fn2: function () {
        fn();
        obj1.fn1();
        console.log(this);
    }
}
obj2.fn2();

我们在全局下调用fn2,其结果为:

//Window
//Object { a: 1, fn1: fn1() }
//Object { a: 2, fn2: fn2() }

由结果分析:

  • fn() => Window.fn(),前缀为Window,fn的this则指向Window;
  • obj1.fn1(),前缀为obj1,fn1的this则指向obj1;
  • obj2.fn2(),前缀为obj2,fn2的this则指向obj2;

三、函数作为Window内置函数的回调函数调用

此时该this指向window( setInterval setTimeout 等)

var timer =null;
timer = setInterval(function(){
    console.log(this);
},1000);

四、箭头函数的this指向存在差异:

箭头函数的this是上一层上下文的this。
箭头函数的this指向在声明函数的时候就固定了,终身不变。
箭头函数时ES6中的内容,本人了解的还不够多,这里就先不赘述了。

五、更改this指向的方法:

1、call

call方法是附加在函数调用后面使用,可以忽略函数本身的 this 指向。
语法: 函数名.call(要改变的 this 指向,要给函数传递的参数1,要给函数传递的参数2, ...)

  var obj = { name: 'Jack' }
  function fn(a, b) {
    console.log(this)
    console.log(a)
    console.log(b)
  }
  fn(1, 2)
  fn.call(obj, 1, 2)
  • fn() 的时候,函数内部的 this 指向 window
  • fn.call(obj, 1, 2) 的时候,函数内部的 this 就指向了 obj 这个对象
  • 使用 call 方法的时候:
    • 会立即执行函数
    • 第一个参数是你要改变的函数内部的 this 指向
    • 第二个参数开始,依次是向函数传递参数

2、apply

apply方法是附加在函数调用后面使用,可以忽略函数本身的 this 指向。
语法: 函数名.apply(要改变的 this 指向,[要给函数传递的参数1, 要给函数传递的参数2, ...])

 var obj = { name: 'Jack' }
  function fn(a, b) {
    console.log(this)
    console.log(a)
    console.log(b)
  }
  fn(1, 2)
  fn.call(obj, [1, 2])
  • fn() 的时候,函数内部的 this 指向 window
  • fn.apply(obj, [1, 2]) 的时候,函数内部的 this 就指向了 obj 这个对象
  • 使用 apply 方法的时候:
    • 会立即执行函数
    • 第一个参数是你要改变的函数内部的 this 指向
    • 第二个参数是一个 数组,数组里面的每一项依次是向函数传递的参数

3、bind

bind方法是把获取到的函数重新封装,返回一个新的函数地址。新地址的this指向改变。
语法:函数.bind(要改变的 this 指向, bind传递的参数1, bind传递参数2, ...);

function fo(a, b){
console.log(this, a, b);
}
var poo = fo.bind( {a : 1} );
console.log(poo)// ƒ fo(a, b) { console.log(this, a, b); }
  • bind绑定的this指向,无法被call和apply更改。
  • 对新返回的函数传递参数,其参数是顺延在bind传递的参数之后的,即bind参数优先。
  • 使用bind方法的时候:
    • 不能再具名函数声明时直接使用bind
    • 可以在赋值式函数声明时直接使用bind
    • 可以在具名函数声明之后使用bind
    • 函数只有一个函数,包装之后使用的还是这个函数

bind、call和apply的不同之处:

  • call有多个参数,第一个参数表示要改变的 this 指向,其余参数对应要给函数传递的参数。
  • apply只有两个参数,第一个参数表示要改变的 this 指向,第二个参数表示对应要给函数传递的参数的数组。

相同之处:

  • call和apply统一都是在函数调用的时候更改this指向的方法。
  • bind绑定的this指向,无法被call和apply更改。
  • bind 返回的是一个新的函数,你必须调用它才会被执行。

以上内容全是本人的一点拙见,如有错误,还望各位大佬指正。

猜你喜欢

转载自www.cnblogs.com/qncsssznds/p/12549424.html