在 TypeScript 函数中显式指定 `this: `
我正在阅读 TypeScript 手册中的《在函数中声明 this》,我被这个例子弄糊涂了:
... 但是在很多情况下,你需要对对象
this的表示有更多控制。JavaScript 规范规定你不能有一个名为this的参数,因此 TypeScript 使用这个语法空间来让你在函数体中声明this的类型。这种模式在回调式 API 中很常见,因为通常由另一个对象来控制何时调用你的函数。interface DB { filterUsers(filter: (this: User) => boolean): User[]; } const db = getDB(); const admins = db.filterUsers(function (this: User) { return this.admin; });
我没有完全理解这一段的意思,特别是如何实现 filterUsers 方法才能让显式的 this: User 生效,所以我决定把这个例子弄成可运行状态:
type User = {
id: number;
admin: boolean;
}
interface DB {
filterUsers(filter: (this: User) => boolean): User[];
}
const users: User[] = [
{ id: 1, admin: true },
{ id: 2, admin: false },
];
const db:DB = {
filterUsers(filter: (this: User) => boolean) {
return users.filter(filter);
}
};
const admins = db.filterUsers(function (this: User) {
return this.admin;
});
console.log(admins);
显然,这个例子行不通,尽管编译器没有报错。从生成的 JavaScript 更能看出——this 在 function (this: User) { return this.admin; } 内不会是 User 实例:
"use strict";
const users = [
{ id: 1, admin: true },
{ id: 2, admin: false },
];
const db = {
filterUsers(filter) {
return users.filter(filter);
}
};
const admins = db.filterUsers(function () {
return this.admin;
});
console.log(admins);
那么,正确实现 filterUsers 的方法是什么,以便显式的 this: User 的概念能够工作并且有用?
答案#1
我建议使用接受 User 对象作为参数的过滤函数,而不是依赖 this:
type User = {
id: number;
admin: boolean;
}
interface DB {
filterUsers(filter: (user: User) => boolean): User[];
}
const users: User[] = [
{ id: 1, admin: true },
{ id: 2, admin: false },
];
const db: DB = {
filterUsers(filter: (user: User) => boolean) {
return users.filter(filter);
}
};
const admins = db.filterUsers((user: User) => {
return user.admin;
});
console.log(admins);
如果你想尝试将参数名称设置为这个,你也可以在 filterUsers 方法内使用 filter.call(user),或者像 @rozsazoltan 那样显式绑定对象。
这确保了在过滤函数内部的 this 指向当前的用户对象。
type User = {
id: number;
admin: boolean;
}
interface DB {
filterUsers(filter: (this: User) => boolean): User[];
}
const users: User[] = [
{ id: 1, admin: true },
{ id: 2, admin: false },
];
const db: DB = {
filterUsers(filter: (this: User) => boolean) {
return users.filter(user => filter.call(user));
}
};
const admins = db.filterUsers(function (this: User) {
return this.admin;
});
console.log(admins);
答案#2
通过使用bind,你可以确保在执行过滤函数时 this 确实是正确的 User 实例。
type User = {
id: number;
admin: boolean;
}
interface DB {
filterUsers(filter: (this: User) => boolean): User[];
}
const users: User[] = [
{ id: 1, admin: true },
{ id: 2, admin: false },
];
const db: DB = {
filterUsers(filter: (this: User) => boolean) {
// need to manually bind the `this` context to each user
return users.filter(user => filter.bind(user)());
}
};
const admins = db.filterUsers(function (this: User) {
return this.admin;
});
console.log(admins);
看起来你似乎无法控制
this的值,因为它的值是自动设置的,但实际上情况并非如此。每个函数都有
.bind方法,它返回一个新的函数,其this被绑定到一个值上。该函数的行为与调用.bind的原函数完全相同,只是this由你设置。无论该函数如何或何时被调用,this始终指向传入的值。来源:如何在回调中访问正确的
this
Felix Kling
