this的五种不同情形
默认情况
默认情况下,纯粹函数调用时,因为没有调用该方法的对象,故而此时的this是undefined,JavaScript 解释器会自动把这个对象指向全局对象 Global,在浏览器环境下,也即 window 对象。
1 2 3 4 5 6 7
   | window.x = "Jackie";
  function func() {   console.log(this.x); }
  func(); 
   | 
 
阮一峰老师的博客中提到:
在严格模式("use strict")下,会禁止this指向全局对象,此时的this会是undefined。
事实上,ES5 的规范 中修改了描述,仅当非严格模式下才会有 this 指向的转变。所以此时this无法改变指向,就会是undefined,但并不是说无法指向全局对象。
1 2 3 4 5
   | "use strict"; function foo() {   console.log(this); } foo.call(this); 
   | 
 
作为对象的方法调用
此时this指向调用这个方法的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13
   | var x = "Property of Window";
  var obj = {}; obj.x = "Property of obj"; obj.f = function () {   console.log(this.x); };
  obj.f(); 
 
  var f = obj.f; f(); 
   | 
 
call、apply和bind的显式绑定
call、apply和bind都可以改变一个函数的this指向。
call和apply
call和apply会将它们的调用对象的this指向它们的第一个参数。
1 2 3 4 5 6 7 8 9 10 11
   | function f() {   console.log(this.x); }
  var x = "Property of Window";
  var obj = {   x: "Property of obj", };
  f.apply(obj); 
  | 
 
当传入的第一个参数为 undefined,或者不传入参数时,在非严格模式下,自动会将this指向全局对象 Global,在浏览器里是window对象,严格模式下则会是undefined:
1 2 3 4 5 6 7 8 9 10 11 12 13
   | function f() {   console.log(this); }
  f.apply();  f.apply(undefined); 
  function ff() {   "use strict";   console.log(this); } ff.apply();  ff.apply(undefined); 
  | 
 
call和apply没有本质区别。唯一的区别在于:
call()方法接受的是若干个参数的列表,而apply()方法接受的是一个包含多个参数的数组。
bind
bind和前面两者也并未有什么本质的区别,只不过bind将第一个参数绑定当调用函数的this上,并将这个函数返回(不执行)。
1 2 3 4 5 6 7 8 9 10 11 12
   | function f() {   console.log(this.x); }
  var x = "Property of Window";
  var obj = {   x: "Property of obj", };
  var ff = f.bind(obj); ff(); 
  | 
 
构造函数
当一个函数被当做构造函数,用new关键字新建一个对象的时候,这个函数内部的this以及原型链上的this都会指向这个新建的对象。
1 2 3 4 5 6 7 8 9 10 11
   | function Jackie(para) {   this.para = para;   console.log(this); } Jackie.prototype.log = function () {   console.log(this); };
  Jackie("hehe");  var p = new Jackie("haha");  p.log(); 
  | 
 
其他值得注意的绑定
放在超时代码里
JavaScript 中超时调用的代码,函数中 this 的值会指向 window 对象,在严格模式下也一样。因为超时调用的代码都会有一个隐式绑定:setTimeout(f, time) == setTimeout(f.bind(window), time)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
   | "use stric"; var x = "Property of Window";
  var obj = {}; obj.x = "Property of obj"; obj.ff = function () {   setTimeout(function () {     console.log(this.x);   }, 100); };
  obj.ff(); 
 
  obj.fff = function () {   var that = this;   setTimeout(function () {     console.log(that.x);   }, 100); }; obj.fff(); 
   | 
 
事件监听函数中的 this
事件监听函数中的this指向监听对象。
1 2 3 4 5 6
   | var one = document.getElementById("one"); one.onclick = function () {   console.log(this); };
  one.click(); 
  | 
 
箭头函数
箭头函数中this的指向,在函数定义时即绑定完毕,且后续无法更改。(即,会跟它的上级作用域的this指针保持一致)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
   | var obj = {   x: 1 }
  var f1 = () => {   console.log(this) } f1.apply(obj) // Window
  var f2 = function () {   var f3 = () => {     console.log(this)   }   return f3 }
  var f4 = f2.apply(obj) f4() // Object {x: 1}
  | 
 
一个更神奇的例子,超时调用的代码在定义时,绑定了this的指向。
1 2 3 4 5 6 7 8 9
   | function foo() {   setTimeout(() => {     console.log("id:", this.id);   }, 100); }
  var id = 21;
  foo.call({ id: 42 }); 
  | 
 
绑定的优先级
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
   | var obj = { x: 0, name: "obj" }; var robj = { x: -1, name: "robj" }; var factory = function (x) {   this.x = x;   console.log(this); };
  var factoryBind = factory.bind(obj); robj.factory = factoryBind; robj.factory(2); 
  factoryBind.call(robj, 3);  console.log(robj);  console.log(obj); 
  var p = new factoryBind(4);  console.log(p);  console.log(obj); 
  | 
 
可以见得,优先级从高到低:
new,构造绑定 
bind,显式绑定 
call/apply,显示绑定 
- 作为方法绑定
 
- 默认绑定