前言
- 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();
|