前言
- 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
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);
严格模式下:
"use strict";
c = 123;
"use strict";
var b = function() { console.log('fun b中的this:', this) }
b();
function test() {
"use strict";
return this;
}
window.test(); // window
// 谁调用函数,函数内部的this指向谁
node
- 下面两种方式才能把变量挂载到 global 上
global.a
a
(不通过 var、let、const,这些声明的变量会挂载到当前模块中而不是全局)
- 严格模式下
- 函数自调用中 global 为 undefined
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();
"use strict";
var b = function() { console.log(this) };
b(); // undefined
class 中 this
- super 做了什么:
- 调用父类的 constructor
- 生成 this ,并把 this 指向 子类
- 如果没有调用 super
- 不会调用父类的 constructor 也就不生成父类的 this
- 子类 constructor 中,访问 this 前,必须得首先调用 super
- 类中是严格模式
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:
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
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(); // apple
son.eat = father.eat;
son.eat(); // orange
如果希望son调用eat时,还是拿的 father 中的 apple,可以在constructor中bind固定this:
class Father {
constructor() {
// 让函数内部的this指向固定
// 这样无论再把eat赋给谁,this都不会改变了
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(); // apple
son.eat = father.eat;
son.eat(); // apple
call、apply、bind 的 this
- call、apply、bind 都能改变 this 指向
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]);
- bind 绑定只能生效一次
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();
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
const test = () => {
console.log(this);
}
test(); // window
"use strict";
const test = () => {
console.log(this);
}
test(); // window
箭头函数忽略任何形式的this指向的改变(apply,call,bind):
var obj = {
a: 1
}
const test = () => {
console.log(this.a);
}
test.call(obj); // undefined
test.apply(obj); // undefined
var test1 = test.bind(obj);
test1(); // undefined
箭头函数中 this 不是在执行期确定,而是声明时就已确定,而且无法更改:
var obj = {
a: 1
}
obj.test = () => {
console.log(this);
}
obj.test(); // window
var obj = {
a: 1
}
obj.test = function() {
var t = () => {
console.log(this); // obj
}
t();
}
obj.test();
var obj = {
a: 1
}
obj.test = function() {
var t1 = () => {
var t2 = () => {
console.log(this); // obj
}
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:指向最近作用域的引用
var obj = {
a: 1,
b: 2,
test: function() {
console.log(this.a); // 1
},
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();
var obj2 = {
a: 1,
b: 2,
test3: function() {
function t() {
// 这个函数t是孤立的,不是 obj2 内部的成员
// 并不存在 obj2.t, t()自调用,默认this为window
console.log(this);
}
t(); // window
}
}
obj2.test3();
var a = 8;
var o = {
a: 10,
b: {
fn: function() {
console.log(this.a);
}
}
}
o.b.fn(); // undefined
原型 this
var obj = {}
obj.__proto__ = {
e: 20
}
console.log(obj.e); // 20 原型继承
var obj = Object.create({
test4: function() {
console.log(this.a + this.b);
}
});
obj.a = 1;
obj.b = 2;
obj.test4(); // 3
构造函数 return this
- new 做了什么
- 实例化一个空对象
- 把需要初始化的属性和方法挂载此对象上
- 隐式的返回此 this
- 如果显式的 return 一个复杂类型的值,会把默认隐式 return 的实例化对象覆盖
function Test() {
this.a = 1;
this.b = 2;
console.log(this); // {a:1,b:2}
return {
c: 3,
d: 4
}
}
var test1 = new Test();
console.log(test1); // {c:3,d:4}
事件处理函数中的 this
onclick
、addEventListener
事件处理函数内部的 this 总是指向被绑定的 DOM 元素
<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:
<button id="btn" onclick="console.log(this)">点我</button>
改变this指向:
如果绑定箭头函数,this 就是 window:
<button id="btn" onclick="(function() { console.log(this) })()">点我</button>
Promise 中回调函数 this 还是 window
练习
var a = 8;
var o = {
a: 10,
b: {
fn: function() {
console.log(this.a);
}
}
}
o.b.fn();
Comments NOTHING