this
this指向
简介
在函数的执行上下文中确定的,也就是说,是在调用函数的时候确定的
普通函数
普通调用
调用的时候决定函数内部的this指向,跟周围上下文环境相关
const foo = function () { |
“.”调用
字面量对象,类实例的”.”调用普通函数,普通函数this指向前面的字面量对象,或类实例
const fooFunc = { |
箭头函数就不一样了,在对象字面量上调用会undefined(当然这里默认是严格模式下,esm模块默认行为),但类定义下确正常
// 箭头函数会在创建函数时确定封闭词法环境,发现外部词法环境是类,做了一个bind绑定 |
Number、String、BigInt、Boolen、Symbol值调用
怎么调用,在这些原始值都有对应的构造函数,在构造函数的原型上添加方法就可以
function getThis() {console.log(this)}; |
原始值会在非严格模式下,this指向一个包装对象。严格模式下指向自身的值
回调
回调的this指向是通过”在于什么时候调用回调”函数决定的
但在setTimeout和setInterval中,普通函数做回调和箭头函数做回调有不同的表现
setTimeout(function () { |
再比如一个Array.prototype下的方法,接收回调,但回调调用时的this指向由这些API自己决定
箭头函数
箭头函数的this表现行为和普通函数不一样,箭头函数会在创建箭头函数时,在周围作用域创建一个封闭词法环境,封闭词法环境可以保证this指向是创建时的值,不会像其他函数在执行时可以更改this
这种表现行为,很容易产生误解,会联想到箭头函数的this指向违反了函数执行上下文词法环境创建时确定this指向。其实是ECMAScript规范指定的行为,根据MDN,this函数会创建一个封闭词法环境,导致执行上下文创建时无法改变this(箭头函数调用时)
const globalObject = this; |
箭头函数使用call、apply
无效果,this指向上文提到不会发生动态改变,调用call() apply()会忽略传入的第一个值(第一个值不正是确认this指向的嘛)
ESM和CJS中
ESM
ESM模块里自动采用了严格模式,顶层的this一辈子都是undefined
CJS
CJS本质每一个模块如下代码
(function (exports, require, module, __filename, __dirname) { |
cjs中的代码其实是在函数中,导出module.exports默认为{}一个空对象,所以顶层的this指向这个空对象
CJS中的箭头函数,普通函数
// cjs中顶层调用 |
绝了,因为箭头函数是定义时形成封闭词法环境,那会儿的this为{}空对象,见上文foo调用时执行上下文为全局执行上下文,全局执行上下文为globalThis
改变函数词法环境的this指向
call、apply
简介
可以在函数调用的时候传入一个context上下文参数以及普通参数args
是Function下面的方法,调用要通过.调用
func.call(context, arg1, arg2, ...) |
区别
call接收多个参数“arg”等作为函数的参数,而apply接收一个类数组对象args作为参数
// worker对象,其中包含一个slow方法,也是slow方法的作用域 |
针对不同函数
- 普通函数:上面的例子已经展示了
- 箭头函数:传入的
thisArg无效,箭头函数中的this还是指向创建环境
bind
返回一个新的函数,可以指定这个函数执行上下文中的this指向,俗称“绑定”
// 函数签名 |
类 Class
js里面,类可以用两种方式表示
// 函数 |
构造函数 constructor
函数式写法得用原型链,参考原型链章节吧,这里只说类的
使用new创建实例,this指向都是这个类实例
- 函数式构造函数:
foo.prototype.contructor = foo(也就是它自身嘛) - 类式:
constructor()
这里面的this一定指向构造的这个新实例(新对象)
super
类的constructor函数中可以使用super函数调用父类的构造函数,也可以使用super.去调用父类的静态方法,或静态属性
其实是换了名字的this
方法
那方法里用到this,怎么找到他的this指向呢?因为方法都是 实例的”.”调用所以又回到上面说的this指向去了(指向这个对象)
静态方法
静态方法中不能访问普通属性,但可以访问静态属性。this 指向呢,同理,执行调用方,调用方是谁?是 Foo 类对象(不是 new Foo 的类实例哦)
class Foo{ |