学习呦
24 11月 2025 13 min read

this

Written by 米豆

this指向

简介

在函数的执行上下文中确定的,也就是说,是在调用函数的时候确定的

普通函数

普通调用

调用的时候决定函数内部的this指向,跟周围上下文环境相关

const foo = function () {
console.log(this);
}

foo() // esm 为 undefined, cjs 为 globalThis, 浏览器 为 window

“.”调用

字面量对象,类实例的”.”调用普通函数,普通函数this指向前面的字面量对象,或类实例

const fooFunc = {
bar : function() {
console.log(this);
}
}

class fooCls{
bar = function() {
console.log(this);
}
}

fooFunc.bar() // { bar: [Function: bar] }
let f = new fooCls
f.bar() // fooCls { bar: [Function: bar] }

箭头函数就不一样了,在对象字面量上调用会undefined(当然这里默认是严格模式下,esm模块默认行为),但类定义下确正常

// 箭头函数会在创建函数时确定封闭词法环境,发现外部词法环境是类,做了一个bind绑定
class fooCls{
barStr = 'bar str'
constructor() {
this.bar = this.bar.bind(this)
}

bar = function () {
console.log(this.barStr);
}
}

Number、String、BigInt、Boolen、Symbol值调用

怎么调用,在这些原始值都有对应的构造函数,在构造函数的原型上添加方法就可以

function getThis() {console.log(this)};

Symbol.prototype.getThis = getThis;
Symbol('foo').getThis();

Number.prototype.getThis = getThis;
(1).getThis();

Boolean.prototype.getThis = getThis;
(true).getThis();

BigInt.prototype.getThis = getThis;
(123456789012345678901234567890n).getThis()

String.prototype.getThis = getThis;
('foo').getThis()

// 结果
// 非严格
// [Symbol: Symbol(foo)]
// [Number: 1]
// [Boolean: true]
// [BigInt: 123456789012345678901234567890n]
// [String: 'foo']

// 严格
// Symbol(foo)
// 1
// true
// 123456789012345678901234567890n
// foo

原始值会在非严格模式下,this指向一个包装对象。严格模式下指向自身的值

回调

回调的this指向是通过”在于什么时候调用回调”函数决定的

但在setTimeout和setInterval中,普通函数做回调和箭头函数做回调有不同的表现

setTimeout(function () {
console.log(this)
})
// this指向 timeout对象
// node "c:\node\index.cjs"
// Timeout {
// _idleTimeout: 1,
// _idlePrev: null,
// ....
// 浏览器指向 window对象

// 箭头函数
setTimeout(() => {
const foo = function () {
console.log(this);
}

foo()
}, 1);
// 箭头函数当回调,箭头函数中调用了foo(),调用这个箭头函数的执行上下文为globalThis
// 输出:globalThis

再比如一个Array.prototype下的方法,接收回调,但回调调用时的this指向由这些API自己决定

箭头函数

箭头函数的this表现行为和普通函数不一样,箭头函数会在创建箭头函数时,在周围作用域创建一个封闭词法环境,封闭词法环境可以保证this指向是创建时的值,不会像其他函数在执行时可以更改this

这种表现行为,很容易产生误解,会联想到箭头函数的this指向违反了函数执行上下文词法环境创建时确定this指向。其实是ECMAScript规范指定的行为,根据MDN,this函数会创建一个封闭词法环境,导致执行上下文创建时无法改变this(箭头函数调用时)

const globalObject = this;
const foo = () => this;
console.log(foo() === globalObject); // this 指向 globalThis 或者 window

const obj2 = {
name: "obj2",
func: () => {
console.log(this);
}
};
obj2.func();//this 也指向globalThis 或者 window


const obj = {
count: 10,
doSomethingLater() {
setTimeout(() => {
// 定义这个箭头函数时,setTimeout也没有上下文,就会找到外面的上下文doSomethingLater
// 使用了外部方法的“obj”上下文
console.log(this.count);
}, 300);

setTimeout(function () {
// 输出undefined,这个函数的this指向globalThis 或者 window
// 不是箭头函数,回调执行的上下文是全局
console.log(this.count);
}, 300);
},
};

// "."调用,作用域为前面对象执行上下文
obj.doSomethingLater(); // 输出 11

箭头函数使用call、apply

无效果,this指向上文提到不会发生动态改变,调用call() apply()会忽略传入的第一个值(第一个值不正是确认this指向的嘛)

ESM和CJS中

ESM

ESM模块里自动采用了严格模式,顶层的this一辈子都是undefined

CJS

CJS本质每一个模块如下代码

(function (exports, require, module, __filename, __dirname) {
function someFunc() {}
exports = someFunc;
// At this point, exports is no longer a shortcut to module.exports, and
// this module will still export an empty default object.
module.exports = someFunc;
})

cjs中的代码其实是在函数中,导出module.exports默认为{}一个空对象,所以顶层的this指向这个空对象

CJS中的箭头函数,普通函数

// cjs中顶层调用
const foo = function () {
console.log(this);
}

const arrow = () => console.log(this);

foo() // globalThis
arrow() // {}

绝了,因为箭头函数是定义时形成封闭词法环境,那会儿的this为{}空对象,见上文
foo调用时执行上下文为全局执行上下文,全局执行上下文为globalThis

改变函数词法环境的this指向

call、apply

简介

可以在函数调用的时候传入一个context上下文参数以及普通参数args
是Function下面的方法,调用要通过.调用

func.call(context, arg1, arg2, ...)
func.apply(context, args)

区别

call接收多个参数“arg”等作为函数的参数,而apply接收一个类数组对象args作为参数

// worker对象,其中包含一个slow方法,也是slow方法的作用域
let worker = {
value: 10,
slow(x) {
return x * this.value; // (*)
}
};

function decorator(func) {
return function(x) {
// 这里是一个闭包,this指向调用对象worker
let result = func.apply(this, [x]); // 现在 "this" 被正确地传递了
return result;
};
}

worker.slow = decorator(worker.slow);
alert( worker.slow(2) ); // 输出 20

针对不同函数

  • 普通函数:上面的例子已经展示了
  • 箭头函数:传入的thisArg无效,箭头函数中的this还是指向创建环境

bind

返回一个新的函数,可以指定这个函数执行上下文中的this指向,俗称“绑定”

// 函数签名
// bind(thisArg, arg1, arg2, /* …, */ argN)

类 Class

js里面,类可以用两种方式表示

// 函数
function foo() {
this.bar = 'bar'
}

// 类
class Foo{
constructor() {
this.bar = 'bar'
}
}

构造函数 constructor

函数式写法得用原型链,参考原型链章节吧,这里只说类的

使用new创建实例,this指向都是这个类实例

  • 函数式构造函数:foo.prototype.contructor = foo(也就是它自身嘛)
  • 类式:constructor()

这里面的this一定指向构造的这个新实例(新对象)

super

类的constructor函数中可以使用super函数调用父类的构造函数,也可以使用super.去调用父类的静态方法,或静态属性
其实是换了名字的this

方法

那方法里用到this,怎么找到他的this指向呢?因为方法都是 实例的”.”调用所以又回到上面说的this指向去了(指向这个对象)

静态方法

静态方法中不能访问普通属性,但可以访问静态属性。this 指向呢,同理,执行调用方,调用方是谁?是 Foo 类对象(不是 new Foo 的类实例哦)

class Foo{
static bar = 'bar'

static getBar() {
console.log(this.bar);
}
}

Foo.getBar() // 输出:bar 里面的 this 指向类对象,不是 new 的类实例

↑