首先确定this的指向问题受哪些因素的影响?
与其他语言相比,函数的 this 关键字在 JavaScript 中的表现略有不同,此外,在严格模式和非严格模式之间也会有一些差别
在绝大多数情况下,函数的调用方式决定了 this
的值(运行时绑定)。this
不能在执行期间被赋值,并且在每次函数被调用时 this
的值也可能会不同。ES5 引入了 bind 方法来设置函数的 this
值,而不用考虑函数如何被调用的。ES2015 引入了箭头函数,箭头函数不提供自身的 this 绑定(this
的值将保持为闭合词法上下文的值)
- 在上边这段描述中我们可以知道
- 大多数情况下
this
的值是在运行时绑定(由函数运行方式决定) this
不能在执行期间赋值- 严格模式和非严格模式下
this
有不同的效果 - 箭头函数中的
this
表现也和第一条的表现不同 - 可以通过
bind
方法设置函数的this值
尝试一下
const test = {
prop: 42,
func: function() {
return this.prop;
},
};
console.log(test.func());
// Expected output: 42
全局执行上下文
无论是否在严格模式下,在全局执行环境中(在任何函数体外部)
this
都指向全局对象。
// 在浏览器中,window 对象同时也是全局对象:
console.log(this === window); // true
a = 37;
console.log(window.a); // 37
this.b = "MDN";
console.log(window.b) // "MDN"
console.log(b)
备注: 你可以使用
globalThis
获取全局对象,无论你的代码是否在当前上下文运行。
函数执行上下文
在函数内部,this
的值取决于函数被调用的方式。
- 直接调用函数
因为下面的代码不在严格模式下,且 this
的值不是由该调用设置的,所以 this
的值默认指向全局对象,浏览器中就是 window
。
function f1(){
return this;
}
//在浏览器中:
f1() === window; //在浏览器中,全局对象是 window
//在 Node 中:
f1() === globalThis;
严格模式下直接调用函数 如果进入执行环境时没有设置 this
的值,this
会保持为 undefined
function f2(){
"use strict"; // 这里是严格模式
return this;
}
f2() === undefined; // true
注意 如果通过window.f2() 那么
this
就指向window了
- 调用对象的方法
this
指向调用方法的对象
function f1(){
return this;
}
var obj = {
f1,
}
//在浏览器中:
f1() === window; //在浏览器中,全局对象是 window
// 以obj的属性调用
obj.f1() === obj // this指向了obj
类上下文中
this
在 类 中的表现与在函数中类似,因为类本质上也是函数,但也有一些区别和注意事项。
在类的构造函数中,this
是一个常规对象。类中所有非静态的方法都会被添加到 this
的原型中:
class Example {
constructor() {
const proto = Object.getPrototypeOf(this);
console.log(Object.getOwnPropertyNames(proto));
}
first(){}
second(){}
static third(){}
}
new Example(); // ['constructor', 'first', 'second']
Object.getPrototypeOf() Object中的静态方法,返回传入对象的原型在
ES5
和Es2015
中表现不同
箭头函数
在箭头函数中,this
与封闭词法环境的this
保持一致。在全局代码中,它将被设置为全局对象:
var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true
备注: 如果将
this
传递给call
、bind
、或者apply
来调用箭头函数,它将被忽略。不过你仍然可以为调用添加参数,不过第一个参数(thisArg
)应该设置为null
。
// 接着上面的代码
// 作为对象的一个方法调用
var obj = {foo: foo};
console.log(obj.foo() === globalObject); // true
// 尝试使用 call 来设定 this
console.log(foo.call(obj) === globalObject); // true
// 尝试使用 bind 来设定 this
foo = foo.bind(obj);
console.log(foo() === globalObject); // true
无论如何,foo
的 this
被设置为他被创建时的环境(在上面的例子中,就是全局对象)。这同样适用于在其他函数内创建的箭头函数:这些箭头函数的this
被设置为封闭的词法环境的。
this 和对象转换
- bind
ECMAScript 5 引入了
Function.prototype.bind()
。调用f.bind(someObject)
会创建一个与f
具有相同函数体和作用域的函数,但是在这个新函数中,this
将永久地被绑定到了bind
的第一个参数,无论这个函数是如何被调用的。
function f(){
return this.a;
}
var g = f.bind({a:"azerty"});
console.log(g()); // azerty
var h = g.bind({a:'yoo'}); // bind 只生效一次!
console.log(h()); // azerty
var o = {a:37, f:f, g:g, h:h};
console.log(o.a, o.f(), o.g(), o.h()); // 37, 37, azerty, azerty
- apply、call
function add(c, d) {
return this.a + this.b + c + d;
}
var o = {a: 1, b: 3};
// 第一个参数是用作“this”的对象
// 其余参数用作函数的参数
add.call(o, 5, 7); // 16
// 第一个参数是用作“this”的对象
// 第二个参数是一个数组,数组中的两个成员用作函数参数
add.apply(o, [10, 20]); // 34
在非严格模式下使用
call
和apply
时,如果用作this
的值不是对象,则会被尝试转换为对象。null
和undefined
被转换为全局对象。原始值如7
或'foo'
会使用相应构造函数转换为对象。因此7
会被转换为new Number(7)
生成的对象,字符串'foo'
会转换为new String('foo')
生成的对象。
总结一下
“this“` 的指向需要考虑当前的执行环境 是否严格模式 是否为箭头函数三个点
- 全局上下文中: 严格模式和非严格模式表现一直都指向 全局对象 浏览器指向
window
node环境指向globalThis
- 函数上下文中:
- 箭头函数中
this
被设置为他被创建时的环境 且不受调用者和bind call apply
的影响 - 普通的函数申明
-
- 直接调用时:严格模式下
this
指向undefined 非严格模式下指向 window
- 直接调用时:严格模式下
-
- 通过对象调用时 指向调用该函数的对象
- 改变函数执行期间的this指向
- call、apply:对箭头函数没有效果
- bind:对箭头函数没有效果,并且只在第一次调用bind时生效
4.可以使用 globalThis
获取全局对象,无论你的代码是否在当前上下文运行
自己做的笔记,如果有错误的地方,希望路过的大佬们指正