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)对象的方法
如果对象的方法里面包含this
,this
的指向就是方法运行时所在的对象。该方法赋值给另一个对象,就会改变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
方法的参数,应该是一个对象。如果参数为空、null
和undefined
,则默认传入全局对象。
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
方法的第一个参数是null
或undefined
,等于将this
绑定到全局对象,函数运行时this
指向顶层对象(浏览器为window
);
function add(x, y) { return x + y; } var plus5 = add.bind(null, 5); plus5(10) // 15