再来摸摸Javascript中this的坑

1,071 阅读4分钟
this 是 Javascript语言 的一个保留关键字,它代表函数运行时自动生成的一个内部对象,只能在函数内部使用。首先要记住的是,函数中的this并不是在创建时确定下来的,而是在函数执行的时候才确定的。这个也是造成使用this时出现很多误解的原因。

本章内容:
  1. 谁调用,谁负责
  2. new的过程
  3. 怪异的return
  4. 被干掉的this
  5. 丢失的this

在开始今天的内容之前,先来看看下面的代码:
// 实例1  
var name = 'a';   


var obj = {   
  name: 'b',   
  getName: function() {   
    console.log(this.name);   
  }  
};  
obj.getName();   


var getName = obj.getName;  
getName();   


var obj2 = (function() {   
  return function() {   
    console.log(this.name);   
  }  
})();  
obj2();  


// 实例2 
function Test() {   
  console.log(this.name);  
}  
Test();   


var test = new Test();   


function Test2() {   
  this.name = 'c';  
}   
var test2 = new Test2();  
console.log(test2.name);  


// 实例3 
function Test3() {   
  this.name = 'd';    
  return {}; // 1 | 'd' | null | undefined   
}  
var test3 = new Test3();  
console.log(test3.name);   


function Test4() {   
  this.name = 'd';    
  return 1;  
}  
var test4 = new Test4();  
console.log(test4.name);  


// 实例4
function Test5() {   
  "use strict";   
  console.log(this.name);  
}  
console.log(Test5());   


function test6() {   
  console.log(this.name);  
}  


// 实例5
var btn = document.querySelector('.btn');  
btn.name = 'button';  
btn.onclick = test6;   


var btn2 = document.querySelector('.btn2');  
btn2.name = 'button';  
btn2.onclick = function() {   
  test6();  
}


保存着你的答案,接下来我们一一分析。

1. 谁调用,谁负责
来回顾一下 实例1:
var name = 'a';   


var obj = {   
  name: 'b',   
  getName: function() {   
    console.log(this.name);   
  }  
};  
obj.getName();  // b   


var getName = obj.getName;  
getName();  // a   


var obj2 = (function() {   
  return function() {   
    console.log(this.name);   
  }  
})();  
obj2();  // a
首先说说obj.getName(),它打印的是 b,为什么呢?因为调用getName()的是obj这家伙,自然,它的this就指向了obj,所以this.name自然就是b。
而接下来的getName()为毛打印的是a呢?因为当前调用getName()的并不是obj了,而是全局变量window,所以打印结果是a
obj2中,我们采用了立即执行函数,它返回一个函数,而当我们调用时,调用它的也是全局变量window,所以其打印的结果也是a。
注: 匿名函数内的this是指向window的,更准确的说是指向调用者。
结论: 谁调用,谁负责

2. new的过程
function Test() {   
  console.log(this.name);  
}  
Test(); // a   


var test = new Test();  // undefined  


function Test2() {   
  this.name = 'c';  
}   


var test2 = new Test2();  
console.log(test2.name);  // c
要搞清楚上面为什么打印c,就有必要了解new的过程
new Test2() 其实执行了三步动作:
var obj  = {};   
obj.__proto__ = Test2.prototype;   
Test2.call(obj);  
首先,创建了一个空对象obj接着,将这个空对象的[[Prototype]](__proto__)成员指向了Test2函数对象prototype成员对象最后,将Test2函数对象的this指针替换成obj
当没有显式返回值或者返回为基本类型、null时,默认将 this 返回。可参考:3. 怪异的return
搞清楚new的过程,相信你对打印出c的原因已经明白了。
结论: 当使用new时,函数内部的this指向已经改变了,并不指向window。

3. 怪异的return

在上面的“new的过程”中,我们了解了new的函数内的this指向,但是,也有一些例外,比如下面:
function Test3() {   
  this.name = 'd';    
  return {};   
}  
var test3 = new Test3();  
console.log(test3.name); // undefined   


function Test4() {   
  this.name = 'd';    
  return 1;  
}  
var test4 = new Test4();  
console.log(test4.name);  // d
可以再试试return 'd' | null | undefined中的一个或更多类型。
结论: 如果return的是一个对象(null除外),那么this指向的这个对象,否则this依旧指向实例对象。

4. 被干掉的this

function Test5() {   
  "use strict";   
  console.log(this.name);  
}  
console.log(Test5()); // Uncaught TypeError: Cannot read property 'name' of undefined
在严格模式下,函数内的this都指向了undefined。 
除了严格模式下,函数内的this被干掉了。在ES6中箭头函数的this同样被干掉了,但是它有点不一样,它可以盗用它最接近一层作用域内的 this


5. 丢失的this


function test6() {   
  console.log(this.name);  
}  
var btn = document.querySelector('.btn');  
btn.name = 'button';  
btn.onclick = test6;  // button   


var btn2 = document.querySelector('.btn2');  
btn2.name = 'button';  
btn2.onclick = function() {   
  test6();  // a  
}

在JavaScript中,事件绑定的函数的this是指向绑定元素的,比如上面的onclick绑定的test6函数内的this是指向btn元素的。而btn2绑定事件为什么打印a呢?回顾一下上面第一节中提到的“谁调用,谁负责”。


此外,setTimeoutsetInterval等函数内this默认的指向是Window。


如有疑问,欢迎在下方的评论区留言!


备注:评论插件已经换成Disqus,需要翻墙 !