this关键词
参考:阮一峰
this没有作用域的限制,嵌套函数不会从调用它的函数中继承this。如果嵌套函数作为方法调用this,this指向调用它的对象,如果嵌套函数作为函数调用,this值不是全局对象就是undefined。
this总是返回
一个对象,this就是属性或方法“当前”所在的对象。
一:由于对象的属性可以赋给另一个对象,所以属性所在的当前对象是可变的,即this的指向是可变的
var A = {
name: '张三',
describe: function () {
return '姓名:'+ this.name;
}
};
var B = {
name: '李四'
};
B.describe = A.describe;
B.describe()
// "姓名:李四"
二:只要函数被赋给另一个变量,this的指向就会变。A.describe被赋值给变量f,内部的this就会指向f运行时所在的对象(本例是顶层对象)。
var A = {
name: '张三',
describe: function () {
return '姓名:'+ this.name;
}
};
var name = '李四';
var f = A.describe;
f() // "姓名:李四"
JavaScript 语言之中,一切皆对象,运行环境也是对象,所以函数都是在某个对象之中运行,this就是函数运行时所在的对象(环境)。这本来并不会让用户糊涂,但是 JavaScript 支持运行环境动态切换,也就是说,this的指向是动态的,没有办法事先确定到底指向哪个对象。
使用场合:
一:全局环境
this === window // true
function f() {
console.log(this === window);
}
f() // true
二:构造函数:指向实例对象
var Obj = function (p) {
this.p = p;
};
var o = new Obj('Hello World!');
o.p // "Hello World!"
三:对象的方法
如果对象的方法里面包含this,this的指向就是方法运行时所在的对象。该方法赋值给另一个对象,就会改变this的指向。
var obj ={
foo: function () {
console.log(this);
}
};
obj.foo() // obj
但是,下面这几种用法,都会改变this的指向。
// 情况一
(obj.foo = obj.foo)() // window
// 情况二
(false || obj.foo)() // window
// 情况三
(1, obj.foo)() // window
上面代码中,obj.foo就是一个值。这个值真正调用的时候,运行环境已经不是obj了,而是全局环境,所以this不再指向obj。
可以这样理解,JavaScript 引擎内部,obj和obj.foo储存在两个内存地址,称为地址一和地址二。obj.foo()这样调用时,是从地址一调用地址二,因此地址二的运行环境是地址一,this指向obj。但是,上面三种情况,都是直接取出地址二进行调用,这样的话,运行环境就是全局环境,因此this指向全局环境。上面三种情况等同于下面的代码。
// 情况一
(obj.foo = function () {
console.log(this);
})()
// 等同于
(function () {
console.log(this);
})()
// 情况二
(false || function () {
console.log(this);
})()
// 情况三
(1, function () {
console.log(this);
})()
如果this所在的方法不在对象的第一层,这时this只是指向当前一层的对象,而不会继承更上面的层。
var a = {
b: {
m: function() {
console.log(this.p);
},
p: 'Hello'
}
};
var hello = a.b.m;
hello() // undefined
上面代码中,m是多层对象内部的一个方法。为求简便,将其赋值给hello变量,结果调用时,this指向了顶层对象。为了避免这个问题,可以只将m所在的对象赋值给hello,这样调用时,this的指向就不会变。
var hello = a.b;
hello.m() // Hello
使用注意点:
一:避免多层this
var o = {
f1: function() {
console.log(this);
var that = this;
var f2 = function() {
console.log(that);
}();
}
}
o.f1()
// Object
// Object
二:避免数组处理方法中的this
数组的map和foreach方法,允许提供一个函数作为参数。这个函数内部不应该使用this。
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
var o = new Object();
o.f = function () {
函数实例的call方法,可以指定函数内部this的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数。 console.log(this === o);
}
// jQuery 的写法
$('#button').on('click', o.f);
控制台会显示false。原因是此时this不再指向o对象,而是指向按钮的 DOM 对象,因为f方法是在按钮对象的环境中被调用的。
绑定this 的方法
JavaScript 提供了call、apply、bind这三个方法,来切换/固定this的指向。
一:Function.prototype.call()
函数实例的call方法,可以指定函数内部this的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数。
var obj = {};
var f = function () {
return this;
};
f() === window // true
f.call(obj) === obj // true
call方法可以改变this的指向,指定this指向对象obj,然后在对象obj的作用域中运行函数f。call方法的参数,应该是一个对象。如果参数为空、null和undefined,则默认传入全局对象.
func.call(thisValue, arg1, arg2, ...)
二:Function.prototype.apply()
apply方法的第一个参数也是this所要指向的那个对象,如果设为null或undefined,则等同于指定全局对象。第二个参数则是一个数组,该数组的所有成员依次作为参数,传入原函数。原函数的参数,在call方法中必须一个个添加,但是在apply方法中,必须以数组形式添加。
三:Function.prototype.bind()
var counter = {
count: 0,
inc: function () {
this.count++;
}
};
var func = counter.inc.bind(counter);
func();
counter.count // 1
代码中,counter.inc方法被赋值给变量func。这时必须用bind方法将inc内部的this,绑定到counter.
bind方法,可以改写一些 JavaScript 原生方法的使用形式。- bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。