前言
关键字
noImplicitThis、any、this
参考资料
- 「知识库 es6(袁进)」回顾 this 指向:4-6. 箭头函数
- Understanding JavaScript Function Invocation and “this”:链接
- noImplicitThis 配置:链接
notes
在 JavaScript 中,this 指向的几种情况
大多数情况下,this 的指向取决于函数的调用方式。
- 如果直接调用函数(全局调用),this 指向全局对象或 undefined(启用严格模式)
- 如果使用
对象.方法
调用,this 指向对象本身。 - 如果是 DOM 事件的处理函数,this 指向事件处理对象。
特殊情况
- 箭头函数,this 在函数声明时确定指向,this 指向函数位置的 this。
- 使用 bind、apply、call 手动绑定 this 对象。
TS 中的 this
配置 noImplicitThis 为 true,表示不允许 this 隐式地指向 any。
在 TS 中,允许在书写函数时,手动声明该函数中的 this 指向。
将 this 作为第一个参数,该参数只用于约束 this,并不是真正的实参,也不会出现在编译结果中。
codes
const u = {
name: "dahuyou",
age: 23,
sayHello() {
console.log(`my name is ${this.name}`);
console.log(`I am ${this.age} years old this year.`);
}
}
u.sayHello();
// my name is dahuyou
// I am 23 years old this year.
const say = u.sayHello;
say(); // => this 指向 global
// my name is undefined
// I am undefined years old this year.
say()
如果直接调用函数(全局调用),this 指向全局对象或 undefined(启用严格模式)u.sayHello()
如果使用对象.方法
调用,this 指向对象本身。
因为此时的宿主环境是 node,在 node 中,全局对象是 global,global 身上没有 name、age 属性。
我们采用这种对象字面量的写法来书写 sayHello 方法,方法中的 this 被识别为 any 类型,这种情况下没法确定 this 的具体指向,得看 sayHello 函数具体是如何被调用的。
所以 TS 没法得知此时的 this 指向,它只能将其识别为 any 类型,我们在写 this.
的时候,也没法给我们提供智能提示。
class User {
constructor(
public name: string,
public age: number
) {}
sayHello() {
console.log(`my name is ${this.name}`);
console.log(`I am ${this.age} years old this year.`);
}
}
const u = new User('dahuyou', 23);
u.sayHello();
// my name is dahuyou
// I am 23 years old this year.
const say = u.sayHello;
say();
// 抛出错误 - 无法从 undefined 身上读取 name 和 age
say()
如果直接调用函数(全局调用),this 指向全局对象或 undefined(启用严格模式)u.sayHello()
如果使用对象.方法
调用,this 指向对象本身。
和上述程序的不同点在于,此时如果全局调用 sayHello 方法,那么会报错。
原因分析:因为在 JS 中,只要是写在 class 中的代码,默认都是启用严格模式的,此时 this 在默认情况下,将别再指向全局,而是指向 undefined。
this 指向
由于我们采用的是类的写法,当我们写类时,我们调用类身上的方法时,一般都是通过 对象.方法
的方式来调用的。为此,TS 会默认类方法中的 this 默认是指向当前类实例的。
this: this
后边的 this,其实就是由 User 类创建的实例对象。
function sayHello() {
console.log(`my name is ${this.name}`);
console.log(`I am ${this.age} years old this year.`);
}
const u = {
name: "dahuyou",
age: 23,
sayHello,
};
sayHello(); // this -> global
// my name is undefined
// I am undefined years old this year.
u.sayHello(); // this -> u
// my name is dahuyou
// I am 23 years old this year.
由于我们没法确认 sayHello
函数具体是如何被调用的,this 的指向存在很多种可能,TS 没法帮我们完成推导,它只能将 this 识别为 any 类型。
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"lib": [
"es2016"
],
"outDir": "./dist",
"strictNullChecks": true,
"strictPropertyInitialization": true,
"removeComments": true,
"noImplicitUseStrict": true,
"noImplicitAny": true,
"noImplicitThis": true
},
"include": [
"./src"
],
}
⚠️ 在测试 noImplicitThis 这个配置项时,和预期结果不一致。写了跟没写一样。
interface IUser {
name: string;
age: number;
sayHello(this: IUser): void;
}
const u: IUser = {
name: "dahuyou",
age: 23,
sayHello() {
console.log(`my name is ${this.name}`);
console.log(`I am ${this.age} years old this year.`);
},
};
u.sayHello();
// my name is dahuyou
// I am 23 years old this year.
const say = u.sayHello;
say(); // ×
sayHello(this: IUser): void;
限制 sayHello 方法中的 this 必须指向 IUser 接口。
将 this 作为第一个参数,该参数只用于约束 this,并不是真正的实参,也不会出现在编译结果中。
报错
预防我们写出 say()
这样的隐患代码。
如果这么调用,this 将默认指向全局对象 global,这和接口约束发生冲突,会报错。