关于this的二三事

2017-05-18  本文已影响0人  字母31

什么是this

this是JS中一个非常重要的关键字。this 就是你 call 一个函数时,传入的 context。任何形式的函数调用都可以写成call形式,传入的第一个参数就是this。

f(a,b)   等价于  f.call(undefined , a , b)
f.m.n(a,b)  等价于  f.m.n.call(f.m , a , b)
fn.call(context , p1 , p2)

所以当我们无法确定this时我们可以转换成call形式来确认this。

如何确定this

通常我们确定this会用以下方式

1、console.log(this)
2、查看API源代码
3、查看API相关文档

this的使用

全局变量

function f() {
  console.log(this); // window
}
var a=1
function f() {
  console.log(this.a); 
}
f()
//1

对象的方法

当A对象的方法被赋予B对象,该方法中的this就从指向A对象变成了指向B对象。所以要特别小心,将某个对象的方法赋值给另一个对象,会改变this的指向。

var obj ={
  foo: function () {
    console.log(this);
  }
};
obj.foo()  // obj

但是,只有这一种用法(直接在obj对象上调用foo方法),this指向obj;其他用法时,this都指向代码块当前所在对象(浏览器为window对象)。

(obj.foo = obj.foo)()
/ 等价于 /
(obj.foo = function () {
  console.log(this);
})()
//window

可以这样理解,obj和obj.foo储存在两个内存地址,简称为M1和M2。只有obj.foo()这样调用时,是从M1调用M2,因此this指向obj。但是,上面情况是直接取出M2进行运算,然后就在全局环境执行运算结果(还是M2),因此this指向全局环境。

避免多层this

由于this的指向是不确定的,所以切勿在函数中包含多层的this。

var o = {
  f1: function () {
    console.log(this);
    var f2 = function () {
      console.log(this);
    }();
  }
}
o.f1()
// Object
// Window

上面代码等价于

var temp = function () {
  console.log(this);
};
var o = {
  f1: function () {
    console.log(this);
    var f2 = temp();
  }
}

当我们把代码修改为

var o = {
  f1: function() {
    console.log(this);
    var that = this;
    var f2 = function() {
      console.log(that);
    }();
  }
}
o.f1()
// Object
// Object

上面代码定义了变量that,固定指向外层的this,然后在内层使用that,就不会发生this指向的改变。

避免数组处理方法中的this

数组的map和foreach方法,允许提供一个函数作为参数。这个函数内部不应该使用this。

var o = {
  v: 'hello',
  p: [ 'a1', 'a2' ],
  f: function f() {
    this.p.forEach(function (item) {
      console.log(this.v + ' ' + item);
    });
  }
}
o.f()
// undefined a1
// undefined a2

上面代码相当于

var temp = function(){
   o.p.forEach(function (item) {
      console.log(this.v + ' ' + item);
    });
} //this指向全局变量
var o = {
  v: 'hello',
  p: [ 'a1', 'a2' ],
  f: temp() 
}
o.f()
// undefined a1
// undefined a2

我们同样可以这样修改

var o = {
  v: 'hello',
  p: [ 'a1', 'a2' ],
  f: function f() {
    var that = this;
    this.p.forEach(function (item) {
      console.log(that.v+' '+item);
    });
  }
}
o.f()
// hello a1
// hello a2

或者将this当作foreach方法的第二个参数,固定它的运行环境。

var o = {
  v: 'hello',
  p: [ 'a1', 'a2' ],
  f: function f() {
    this.p.forEach(function (item) {
      console.log(this.v + ' ' + item);
    }, this);
  }
}
o.f()
// hello a1
// hello a2

bind方法

bind方法用于将函数体内的this绑定到某个对象,然后返回一个新函数。

var counter = {
  count: 0,
  inc: function () {
    this.count++;
  }
};
var func = counter.inc;
func();
counter.count // 0
count // NaN

上面代码中,函数func是在全局环境中运行的,这时inc内部的this指向顶层对象window,所以counter.count是不会变的,反而创建了一个全局变量count。因为window.count原来等于undefined,进行递增运算后undefined++就等于NaN。

var func = counter.inc.bind(counter);
func();
counter.count
//1

使用bind方法将inc方法绑定到counter以后,再运行func就会得到正确结果。

小结

对于this,我们只要记得其本质就是call函数时传入的第一个参数,如果函数调用形式不是 call 形式,请将其转换为 call 形式,函数的this值可以用call改变,也可以用bind改变默认的this值。

上一篇 下一篇

猜你喜欢

热点阅读