首先,this的指向在函数定义的时候是肯定不了的,只有函数执行的时候才能肯定this到底指向谁,实际上this的最终指向的是那个调用它的对象(虽然在不少状况下那样去理解不会出什么问题,可是实际上会有些特殊存在),那么接下来我会深刻的探讨这个问题。chrome
例子1:数组
function a(){ var user = " 小明"; console.log(this.user); //undefined console.log(this); //Window } a();
按照咱们上面说的this最终指向的是调用它的对象,这里的函数a实际是被Window对象所点出来的,下面的代码就能够证实。babel
function a(){ var user = " 小明"; console.log(this.user); //undefined console.log(this); //Window } window.a();
例子2:app
var o = {
user:" 小明",
fn:function(){
console.log(this.user); // 小明
}
}
o.fn();
这里的this指向的是对象o,由于你调用这个fn是经过o.fn()执行的,那天然指向就是对象o,this的指向在函数建立的时候是决定不了的,在调用的时候才能决定,谁调用的就指向谁。函数
若是要完全的搞懂this必须看接下来的几个例子post
例子3:this
var o = {
user:" 小明",
fn:function(){
console.log(this.user); // 小明
}
}
window.o.fn();
这段代码和上面的那段代码几乎是同样的,可是这里的this为何不是指向window,若是按照上面的理论,最终this指向的是调用它的对象,这里先说个而外话,window是js中的全局对象,咱们建立的变量其实是给window添加属性,因此这里能够用window点o对象。es5
这里先不解释为何上面的那段代码this为何没有指向window,咱们再来看一段代码。spa
var o = {
a:10,
b:{
a:12,
fn:function(){
console.log(this.a); //12
}
}
}
o.b.fn();
这里一样也是对象o点出来的,可是一样this并无执行它,那你确定会说我一开始说的那些不就都是错误的吗?其实也不是,只是一开始说的不许确,接下来我将补充一句话,我相信你就能够完全的理解this的指向的问题。
状况1:若是一个函数中有this,可是它没有被上一级的对象所调用,那么this指向的就是window,这里须要说明在js的严格版中this指向的不是window。
状况2:若是一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。
状况3:若是一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象,例子3能够证实。
接下来咱们继续看几个例子。
var o = {
a:10,
b:{
// a:12,
fn:function(){
console.log(this.a); //undefined
}
}
}
o.b.fn();
尽管对象b中没有属性a,这个this指向的也是对象b,由于this只会指向它的上一级对象,无论这个对象中有没有this要的东西。
还有一种比较特殊的状况,例子4:
var o = { a:10, b:{ a:12, fn:function(){ console.log(this.a); //undefined console.log(this); //window } } } var j = o.b.fn; j();
这里this指向的是window,这句话一样相当重要”this永远指向的是调用它的对象,也就是看它执行的时候是谁调用就指向谁“,例子4中虽然函数fn是被对象b所引用,可是在将fn赋值给变量j的时候并无执行,因此最终指向的是window,这和例子3是不同的,例子3是直接执行了fn。
构造函数版this:
function Fn(){
this.user = " 小明";
}
var a = new Fn();
console.log(a.user); // 小明
这里之因此对象a能够点出函数Fn里面的user是由于new关键字能够改变this的指向,理解这句话能够想一想咱们的例子3,咱们这里用变量a建立了一个Fn的实例(至关于复制了一份Fn到对象a里面),此时仅仅只是建立,并无执行,而调用这个函数Fn的是对象a,那么this指向的天然是对象a,那么为何对象a中会有user,由于你已经复制了一份Fn函数到对象a中,用了new关键字就等同于复制了一份。
当this碰到return时
function fn()
{
this.user = ' 小明';
return {};
}
var a = new fn;
console.log(a.user); //undefined
function fn()
{
this.user = ' 小明';
return function(){};
}
var a = new fn;
console.log(a.user); //undefined
function fn()
{
this.user = ' 小明';
return 1;
}
var a = new fn;
console.log(a.user); // 小明
function fn()
{
this.user = ' 小明';
return undefined;
}
var a = new fn;
console.log(a.user); // 小明
从这些例子中能够总结出,若是返回值是一个对象,那么this指向的就是那个返回的对象,若是返回值不是一个对象那么this仍是指向函数的实例。
function fn()
{
this.user = ' 小明';
return undefined;
}
var a = new fn;
console.log(a); //fn {user: " 小明"}
特殊状况null也是对象,可是在这里this仍是指向那个函数的实例。
function fn()
{
this.user = ' 小明';
return null;
}
var a = new fn;
console.log(a.user); // 小明
当this遇到call、apply、bind方法时
一、call()
var a = {
user:"小明",
fn:function(){
console.log(this.user); //小明
}
}
var b = a.fn;
b.call(a);
经过在call方法,给第一个参数添加要把b添加到哪一个环境中,简单来讲,this就会指向那个对象。
call方法除了第一个参数之外还能够添加多个参数,以下:
var a = { user:"小明", fn:function(e,ee){ console.log(this.user); //小明 console.log(e+ee); //3 } } var b = a.fn; b.call(a,1,2);
二、apply()
apply方法和call方法有些类似,它也能够改变this的指向
var a = {
user:"小明",
fn:function(){
console.log(this.user); //小明
}
}
var b = a.fn;
b.apply(a);
一样apply也能够有多个参数,可是不一样的是,第二个参数必须是一个数组,以下:
var a = { user:"小明", fn:function(e,ee){ console.log(this.user); //小明 console.log(e+ee); //11 } } var b = a.fn; b.apply(a,[10,1]);
//注意若是call和apply的第一个参数写的是null,那么this指向的是window对象
var a = {
user:"小明",
fn:function(){
console.log(this); //Window {external: Object, chrome: Object, document: document, a: Object, speechSynthesis: SpeechSynthesis…}
}
}
var b = a.fn;
b.apply(null);
三、bind()
bind方法和call、apply方法有些不一样,可是无论怎么说它们均可以用来改变this的指向。
先来讲说它们的不一样吧。
var a = { user:"小明", fn:function(){ console.log(this.user); } } var b = a.fn; b.bind(a);
咱们发现代码没有被打印,对,这就是bind和call、apply方法的不一样,实际上bind方法返回的是一个修改事后的函数。
var a = {
user:"小明",
fn:function(){
console.log(this.user);
}
}
var b = a.fn;
var c = b.bind(a);
console.log(c); //function() { [native code] }
那么咱们如今执行一下函数c看看,能不能打印出对象a里面的user
var a = {
user:"小明",
fn:function(){
console.log(this.user); //小明
}
}
var b = a.fn;
var c = b.bind(a);
c();
总结:call和apply都是改变上下文中的this并当即执行这个函数,bind方法可让对应的函数想何时调就何时调用,而且能够将参数在执行的时候添加,这是它们的区别,根据本身的实际状况来选择使用。
this遇到-定时器以及箭头函数时
使用js中的定时器(setInterval,setTimeout),很容易会遇到this指向的问题。
直接上例子:
var name = 'my name is window';
var obj = {
name: 'my name is obj',
fn: function () {
var timer = null;
clearInterval(timer);
timer = setInterval(function () {
console.log(this.name); //my name is window
}, 1000)
}
}
在这里,从this.name能够看出this的指向是window。
若是没有特殊指向,setInterval和setTimeout的回调函数中this的指向都是window。这是由于JS的定时器方法是定义在window下的。可是平时不少场景下,都须要修改this的指向。这里总结了几种:
一、最经常使用的方法:在外部函数中将this存为一个变量,回调函数中使用该变量,而不是直接使用this。
var name = 'my name is window';
var obj = {
name: 'my name is obj',
fn: function () {
var that = this;
var timer = null;
clearInterval(timer);
timer = setInterval(function () {
console.log(that.name); //my name is obj
}, 1000)
}
}
在fn中加了var that = this; 回调函数中使用that代替this便可。这种方法最多见,使用也最普遍。
二、使用bind()方法(bind()为ES5的标准,低版本IE下有兼容问题,能够引入es5-shim.js解决)
bind()的做用相似call和apply,都是修改this指向。可是call和apply是修改this指向后函数会当即执行,而bind则是返回一个新的函数,它会建立一个与原来函数主体相同的新函数,新函数中的this指向传入的对象。
var name = 'my name is window';
var obj = {
name: 'my name is obj',
fn: function () {
var timer = null;
clearInterval(timer);
timer = setInterval(function () {
console.log(this.name); //my name is obj
}.bind(this), 1000)
}
}
在这里为何不能用call和apply,是由于call和apply不是返回函数,而是当即执行函数,那么,就失去了定时器的做用。
三、使用es6的箭头函数:箭头函数的最大做用就是this指向。
var name = 'my name is window';
var obj = {
name: 'my name is obj',
fn: function () {
var timer = null;
clearInterval(timer);
timer = setInterval(() => {
console.log(this.name); //my name is obj
}, 1000)
}
}
箭头函数没有本身的this,它的this继承自外部函数的做用域。因此,在该例中,定时器回调函数中的this,是继承了fn的this。固然箭头函数也有兼容问题,要是兼容低版本ie,须要使用babel编译才能够。