前言
- this 是 JavaScript 的关键字
 
- 是 当前环境 执行期上下文对象 的 一个属性
 
- this明确指向的时机:执行期
 
- this 在不同环境、不同作用域下,表现不同
 
全局this
🌈获取全局对象this
- web 下的全局 this: 
window、self、frames、this 
- node 下的全局 this: 
global、globalThis 
- worker 下的全局 this: 
self 
- 通用:
globalThis 
web
- 全局 this = window
 
- 严格模式下
- 未通过 var 声明的变量会报错
 
- 函数自调用中 this 为 undefined
 
 
1 2 3 4 5 6 7
   | var a = 1; var b = function() { console.log('fun b中的this:', this) } c = {}; console.log('this === window', this === window); console.log('window.a === a:', window.a === a); b(); console.log('window.c === this.c:', window.c === this.c);
   | 
 
严格模式下:
1 2 3
   | "use strict"; var b = function() { console.log('fun b中的this:', this) } b();
   | 
 
1 2 3 4 5 6 7 8
   | function test() {   "use strict";   return this; }
  window.test(); 
 
 
  | 
 
node
- 下面两种方式才能把变量挂载到 global 上
global.a 
a (不通过 var、let、const,这些声明的变量会挂载到当前模块中而不是全局) 
 
- 严格模式下
- 函数自调用中 global 为 undefined
 
 
1 2 3 4 5 6 7 8 9 10
   | var a = 1; b = function() { console.log(this) }; global.c = 2; console.log(a); console.log(b); console.log(c); console.log(global.a); console.log(global.b); console.log(global.c); b();
   | 
 
1 2 3
   | "use strict"; var b = function() { console.log(this) }; b(); 
   | 
 
🌈class 中 this
- super 做了什么:
- 调用父类的 constructor
 
- 生成 this ,并把 this 指向 子类
 
 
- 如果没有调用 super
- 不会调用父类的 constructor 也就不生成父类的 this
 
 
- 子类 constructor 中,访问 this 前,必须得首先调用 super
 
- 类中是严格模式
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
   | class Father {   constructor(age) {     this.age = age;   }   swim() {     console.log('Go swimming!!!');   } }
  class Son extends Father {   constructor() {     super(23);     this.hobby = 'travel';     console.log(this.age);   }   study() {     console.log(this);     this.swim();   } }
  const son = new Son(); son.study();
  | 
 
不能调换super顺序,得首先调用 super:
1 2 3 4 5 6
   | class Son extends Father {   constructor() {     this.hobby = 'travel';     super(23);   } }
  | 
 
原因是按照正常顺序:
- super() 被调用
 
- 调用父类 constructor
 
- 实例化父类 this,给父类 this 挂上 age 属性
 
- 再执行子类 constructor 中代码
 
- 先把父类 this 指向子类实例化的 this
 
- 给子类 this 添加上 hobby 属性
 
- 现在子类上有两个属性了,分别是 age、hobby
 
而如果先执行子类 constructor 中代码,后执行 super,就会导致先实例化了子类 this,再实例化父类 this,这样逻辑就不对了。
类之间操作this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
   | class Father { 	get fruit() {   	return 'apple';   }   eat() {   	console.log('I am eating an ' + this.fruit);   } }
  class Son { 	get fruit() {   	return 'orange';   } }
  const father = new Father(); const son = new Son();
  father.eat();  son.eat = father.eat; son.eat(); 
  | 
 
如果希望son调用eat时,还是拿的 father 中的 apple,可以在constructor中bind固定this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
   | class Father {   constructor() {               this.eat = this.eat.bind(this);   } 	get fruit() {   	return 'apple';   }   eat() {   	console.log('I am eating an ' + this.fruit);   } }
  class Son { 	get fruit() {   	return 'orange';   } }
  const father = new Father(); const son = new Son();
  father.eat();  son.eat = father.eat; son.eat(); 
  | 
 
🌈 call、apply、bind 的 this
- call、apply、bind 都能改变 this 指向
 
1 2 3 4 5 6 7 8 9 10 11 12
   | var obj = { 	a: 1 } var a = 2;
  function test(b, c) { 	console.log(this); }
  test(); test.call(obj, 3, 4); test.apply(obj, [3, 4]);
  | 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
   | var obj = { 	a: 1 } var obj2 = { 	a: 100 }
  function test(b, c) { 	console.log(this, b, c); }
  var test1 = test.bind(obj, 3, 4); test1(); var test2 = test1.bind(obj2, 3, 4); test2();
  | 
 
1 2 3 4 5 6 7 8 9 10 11 12 13
   | var obj = { 	a: 1 } var obj2 = { 	a: 100 }
  function test(b, c) { 	console.log(this, b, c); }
  var test1 = test.bind(obj2, 3, 4).bind(obj, 3, 4); test1();
  | 
 
箭头函数中 this
- 箭头函数本身没有 this
 
- 箭头函数内部 this 指向外层作用域 this
 
- 箭头函数中 this 不是在执行期确定,而是声明时就已确定,而且无法更改
 
- 箭头函数忽略任何形式的this指向的改变(apply,call,bind)
 
- 箭头函数无法通过 new 实例化
 
不管是否是严格模式,全局下箭头函数的this都为window
1 2 3 4 5 6 7 8 9
   | const test = () => { 	console.log(this); } test();  "use strict"; const test = () => { 	console.log(this); } test(); 
  | 
 
箭头函数忽略任何形式的this指向的改变(apply,call,bind):
1 2 3 4 5 6 7 8 9 10 11
   | var obj = { 	a: 1 }
  const test = () => { 	console.log(this.a); } test.call(obj);  test.apply(obj);  var test1 = test.bind(obj); test1(); 
  | 
 
箭头函数中 this 不是在执行期确定,而是声明时就已确定,而且无法更改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
   | var obj = { 	a: 1 }
  obj.test = () => { 	console.log(this); } obj.test();  var obj = { 	a: 1 }
  obj.test = function() { 	var t = () => {   	console.log(this);    }   t(); } obj.test(); var obj = { 	a: 1 }
  obj.test = function() { 	var t1 = () => {   	var t2 = () => {     	console.log(this);      }     t2();   }   t1(); } obj.test(); var obj = { 	a: 1 }
  obj.test = function() { 	var t1 = function() {   	var t2 = () => {     	console.log(this);     }     t2();   }   t1(); } obj.test();
  | 
 
对象中的 this
- this指向的基本原则:谁调用,this指向谁
 
- 对象方法内部的this:指向最近作用域的引用
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
   | var obj = { 	a: 1,   b: 2,   test: function() {   	console.log(this.a);    },   test2: test2,   c: {   	d: 4,     test3: function() {     	console.log(this);       console.log(this.d);     }   } } function test2() { 	console.log(this.b); } obj.test(); obj.test2(); obj.c.test3();
  | 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
   | var obj2 = { 	a: 1,   b: 2,   test3: function() {   	function t() {                   	console.log(this);     }     t();    } } obj2.test3(); var a = 8; var o = { 	a: 10,   b: {   	fn: function() {     	console.log(this.a);     }   } } o.b.fn(); 
  | 
 
原型this
1 2 3 4 5 6 7 8 9 10 11 12 13
   | var obj = {} obj.__proto__ = { 	e: 20 } console.log(obj.e);  var obj = Object.create({ 	test4: function() {   	console.log(this.a + this.b);   } }); obj.a = 1; obj.b = 2; obj.test4(); 
  | 
 
🌈构造函数 return this
1 2 3 4 5 6 7 8 9 10 11 12
   | function Test() { 	this.a = 1;   this.b = 2;   console.log(this);       return {   	c: 3,     d: 4   } } var test1 = new Test(); console.log(test1); 
  | 
 
🌈事件处理函数中的this
- onclick、addEventListener事件处理函数内部的this总是指向被绑定的DOM元素
 
1 2 3 4 5 6 7 8 9 10
   | <body>   <button id="btn">点我</button> </body> var btn = document.getElementById('btn'); btn.onclick = function() {   console.log(this); // btn元素 } btn.addEventListener('click', function() {   console.log(this); // btn元素 }, false);
   | 
 
直接在元素上绑定,this还是btn:
1
   | <button id="btn" onclick="console.log(this)">点我</button>
   | 
 
改变this指向:
如果绑定箭头函数,this就是window:
1
   | <button id="btn" onclick="(function() { console.log(this) })()">点我</button>
  | 
 
Promise中回调函数this还是window
练习
1 2 3 4 5 6 7 8 9 10
   | var a = 8; var o = {   a: 10,   b: {     fn: function() {       console.log(this.a);     }   } } o.b.fn();
   |