this关键字的理解与总结

1.this关键字的含义

简单说,this就是属性或方法“当前”所在的对象。

this都有一个共同点:它总是返回一个对象。

举例:

var A = {
  name: '张三',
  describe: function () {
    return '姓名:'+ this.name;
  }
};
A.describe();
// '姓名:张三'
由于对象的属性可以赋给另一个对象,所以属性所在的当前对象是可变的,即this的指向是可变的。
var A = {
  name: '张三',
  describe: function () {
    return '姓名:'+ this.name;
  }
};

var B = {
  name: '李四'
};

B.describe = A.describe;
B.describe()
// "姓名:李四"

重构如下

function f() {
  return '姓名:'+ this.name;
}

var A = {
  name: '张三',
  describe: f
};

var B = {
  name: '李四',
  describe: f
};

A.describe() // "姓名:张三"
B.describe() // "姓名:李四"

只要函数被赋给另一个变量,this的指向就会变。

var A = {
  name: '张三',
  describe: function () {
    return '姓名:'+ this.name;
  }
};

var name = '李四';
var f = A.describe;
f() // "姓名:李四"

A.describe被赋值给变量f,内部的this就会指向f运行时所在的对象(本例是顶层对象);

一切皆对象,运行环境也是对象,所以函数都是在某个对象之中运行,this就是函数运行时所在的对象(环境)。

2.this的实质

var obj = { foo:  5 };

原始的对象以字典结构保存;

引擎先从obj拿到内存地址,然后再从该地址读出原始的对象,返回它的foo属性。

{
  foo: {
    [[value]]: 5
    [[writable]]: true
    [[enumerable]]: true
    [[configurable]]: true
  }
}
属性值是一个函数的情况
var obj = { foo: function () {} };
{
  foo: {
    [[value]]: 函数的地址
    ...
  }
}

由于函数是一个单独的值,所以它可以在不同的环境(上下文)执行。

它的设计目的就是在函数体内部,指代函数当前的运行环境。

3.使用场合

(1)全局环境

全局环境使用this,它指的就是顶层对象window

this === window // true

(2)构造函数

构造函数中的this,指的是实例对象。

var Obj = function (p) {
  this.p = p;
};
var o = new Obj('Hello World!');
o.p // "Hello World!"

(3)对象的方法

如果对象的方法里面包含thisthis的指向就是方法运行时所在的对象。该方法赋值给另一个对象,就会改变this的指向。

如果this所在的方法不在对象的第一层,这时this只是指向当前一层的对象,而不会继承更上面的层。


var a = {
  p: 'Hello',
  b: {
    m: function() {
      console.log(this.p);
    }
  }
};

a.b.m() // undefined
因此,实际执行是下面这个代码
var b = {
  m: function() {
   console.log(this.p);
  }
};

var a = {
  p: 'Hello',
  b: b
};

(a.b).m() // 等同于 b.m()
想要达到预期效果是
var a = {
  b: {
    m: function() {
      console.log(this.p);
    },
    p: 'Hello'
  }
};

如果这时将嵌套对象内部的方法赋值给一个变量,this依然会指向全局对象。

var a = {
  b: {
    m: function() {
      console.log(this.p);
    },
    p: 'Hello'
  }
};

var hello = a.b.m;
hello() // undefined

4. 1)避免多层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();
  }
}

一个解决方法是在第二层改用一个指向外层this的变量。

that,固定指向外层的this,然后在内层使用that,就不会发生this指向的改变。

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

o.f1()
// Object
// Object

4. 2)避免数组处理方法中的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

解决方法

1.使用中间变量固定this;

2.另一种方法是将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

3.避免在回调函数中使用this;

var o = new Object();
o.f = function () {
  console.log(this === o);
}

// jQuery 的写法
$('#button').on('click', o.f);

上面代码中,点击按钮以后,控制台会显示false。原因是此时this不再指向o对象,而是指向按钮的 DOM 对象,因为f方法是在按钮对象的环境中被调用的。

为了解决这个问题,可以采用下面的一些方法对this进行绑定,也就是使得this固定指向某个对象,减少不确定性。

1.Function.prototype.call()

call方法的参数,应该是一个对象。如果参数为空、nullundefined,则默认传入全局对象。

2.Function.prototype.apply()

3.Function.prototype.bind()

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

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

如果bind方法的第一个参数是nullundefined,等于将this绑定到全局对象,函数运行时this指向顶层对象(浏览器为window);

function add(x, y) {
  return x + y;
}

var plus5 = add.bind(null, 5);
plus5(10) // 15

 

 
 
 
 
 
posted @ 2018-12-13 00:59  jiaqiq  阅读(931)  评论(0编辑  收藏  举报