如何在类型方法中使用另一个类方法和this上下文? [英] How to use another class method and the this context in a type method?
问题描述
我想重写JavaScript方法,下面在TypeScript中进一步介绍。
我很想像这样做,就像这样:
I want to rewrite the JavaScript "method" further below in TypeScript. I am tempted to do it as class, like this:
// export default class
export default class GroupItemMetadataProvider1
{
protected m_instance;
protected _grid;
protected _defaults;
protected m_options;
constructor(options)
{
this.m_instance = this;
this._defaults = {
groupCssClass: "slick-group",
groupTitleCssClass: "slick-group-title",
totalsCssClass: "slick-group-totals",
groupFocusable: true,
totalsFocusable: false,
toggleCssClass: "slick-group-toggle",
toggleExpandedCssClass: "expanded",
toggleCollapsedCssClass: "collapsed",
enableExpandCollapse: true,
groupFormatter: this.defaultGroupCellFormatter,
totalsFormatter: this.defaultTotalsCellFormatter
};
options = $.extend(true, {}, this._defaults, options);
this.m_options = options;
}
protected defaultGroupCellFormatter(row, cell, value, columnDef, item)
{
if (!this.m_options.enableExpandCollapse)
{
return item.title;
}
let indentation = item.level * 15 + "px";
return "<span class='" + this.m_options.toggleCssClass + " " +
(item.collapsed ? this.m_options.toggleCollapsedCssClass : this.m_options.toggleExpandedCssClass) +
"' style='margin-left:" + indentation + "'>" +
"</span>" +
"<span class='" + this.m_options.groupTitleCssClass + "' level='" + item.level + "'>" +
item.title +
"</span>";
}
protected defaultTotalsCellFormatter(row, cell, value, columnDef, item)
{
return (columnDef.groupTotalsFormatter && columnDef.groupTotalsFormatter(item, columnDef)) || "";
}
protected init(grid)
{
this._grid = grid;
this._grid.onClick.subscribe(this.handleGridClick);
this._grid.onKeyDown.subscribe(this.handleGridKeyDown);
}
protected destroy()
{
if (this._grid)
{
this._grid.onClick.unsubscribe(this.handleGridClick);
this._grid.onKeyDown.unsubscribe(this.handleGridKeyDown);
}
}
protected handleGridClick(e, args)
{
let context = (<any>this);
let item = context.getDataItem(args.row);
if (item && item instanceof Slick.Group && $(e.target).hasClass(this.m_options.toggleCssClass))
{
let range = this._grid.getRenderedRange();
context.getData().setRefreshHints({
ignoreDiffsBefore: range.top,
ignoreDiffsAfter: range.bottom + 1
});
if (item.collapsed)
{
context.getData().expandGroup(item.groupingKey);
} else
{
context.getData().collapseGroup(item.groupingKey);
}
e.stopImmediatePropagation();
e.preventDefault();
}
}
// TODO: add -/+ handling
protected handleGridKeyDown(e)
{
let context = (<any>this);
if (this.m_options.enableExpandCollapse && (e.which == Slick.keyCode.SPACE))
{
let activeCell = context.getActiveCell();
if (activeCell)
{
let item = context.getDataItem(activeCell.row);
if (item && item instanceof Slick.Group)
{
let range = this._grid.getRenderedRange();
context.getData().setRefreshHints({
ignoreDiffsBefore: range.top,
ignoreDiffsAfter: range.bottom + 1
});
if (item.collapsed)
{
context.getData().expandGroup(item.groupingKey);
} else
{
context.getData().collapseGroup(item.groupingKey);
}
e.stopImmediatePropagation();
e.preventDefault();
}
}
}
}
public getGroupRowMetadata(item)
{
return {
selectable: false,
focusable: this.m_options.groupFocusable,
cssClasses: this.m_options.groupCssClass,
columns: {
0: {
colspan: "*",
formatter: this.m_options.groupFormatter,
editor: null
}
}
};
}
public getTotalsRowMetadata(item)
{
return {
selectable: false,
focusable: this.m_options.totalsFocusable,
cssClasses: this.m_options.totalsCssClass,
formatter: this.m_options.totalsFormatter,
editor: null
};
}
}
但是, handleGridClick
& handleGridKeyDown
都引用 this事件上下文以及 this类上下文来获取选项。
However, handleGridClick
& handleGridKeyDown
both reference the 'this' "event-context", as well as the 'this' class-context to get the options.
问题是,我不能仅将this绑定到构造函数中,因为否则,该对象的this-context是错误的。我该怎么做?
Problem is, I can't just bind the this in the constructor, because otherwise, the this-context of the object is wrong. How can I do this?
这是普通的JavaScript变体:
This is the plain JavaScript variant:
// import $ from '../../wwwroot/jQuery-3.3.js';
import Slick from './slick.core.js';
export default GroupItemMetadataProvider;
/***
* Provides item metadata for group (Slick.Group) and totals (Slick.Totals) rows produced by the DataView.
* This metadata overrides the default behavior and formatting of those rows so that they appear and function
* correctly when processed by the grid.
*
* This class also acts as a grid plugin providing event handlers to expand & collapse groups.
* If "grid.registerPlugin(...)" is not called, expand & collapse will not work.
*
* @class GroupItemMetadataProvider
* @module Data
* @namespace Slick.Data
* @constructor
* @param options
*/
function GroupItemMetadataProvider(options)
{
let _grid;
let _defaults = {
groupCssClass: "slick-group",
groupTitleCssClass: "slick-group-title",
totalsCssClass: "slick-group-totals",
groupFocusable: true,
totalsFocusable: false,
toggleCssClass: "slick-group-toggle",
toggleExpandedCssClass: "expanded",
toggleCollapsedCssClass: "collapsed",
enableExpandCollapse: true,
groupFormatter: defaultGroupCellFormatter,
totalsFormatter: defaultTotalsCellFormatter
};
options = $.extend(true, {}, _defaults, options);
function defaultGroupCellFormatter(row, cell, value, columnDef, item)
{
if (!options.enableExpandCollapse)
{
return item.title;
}
let indentation = item.level * 15 + "px";
return "<span class='" + options.toggleCssClass + " " +
(item.collapsed ? options.toggleCollapsedCssClass : options.toggleExpandedCssClass) +
"' style='margin-left:" + indentation + "'>" +
"</span>" +
"<span class='" + options.groupTitleCssClass + "' level='" + item.level + "'>" +
item.title +
"</span>";
}
function defaultTotalsCellFormatter(row, cell, value, columnDef, item)
{
return (columnDef.groupTotalsFormatter && columnDef.groupTotalsFormatter(item, columnDef)) || "";
}
function init(grid)
{
_grid = grid;
_grid.onClick.subscribe(handleGridClick);
_grid.onKeyDown.subscribe(handleGridKeyDown);
}
function destroy()
{
if (_grid)
{
_grid.onClick.unsubscribe(handleGridClick);
_grid.onKeyDown.unsubscribe(handleGridKeyDown);
}
}
function handleGridClick(e, args)
{
let item = this.getDataItem(args.row);
if (item && item instanceof Slick.Group && $(e.target).hasClass(options.toggleCssClass))
{
let range = _grid.getRenderedRange();
this.getData().setRefreshHints({
ignoreDiffsBefore: range.top,
ignoreDiffsAfter: range.bottom + 1
});
if (item.collapsed)
{
this.getData().expandGroup(item.groupingKey);
} else
{
this.getData().collapseGroup(item.groupingKey);
}
e.stopImmediatePropagation();
e.preventDefault();
}
}
// TODO: add -/+ handling
function handleGridKeyDown(e)
{
if (options.enableExpandCollapse && (e.which == Slick.keyCode.SPACE))
{
let activeCell = this.getActiveCell();
if (activeCell)
{
let item = this.getDataItem(activeCell.row);
if (item && item instanceof Slick.Group)
{
let range = _grid.getRenderedRange();
this.getData().setRefreshHints({
ignoreDiffsBefore: range.top,
ignoreDiffsAfter: range.bottom + 1
});
if (item.collapsed)
{
this.getData().expandGroup(item.groupingKey);
} else
{
this.getData().collapseGroup(item.groupingKey);
}
e.stopImmediatePropagation();
e.preventDefault();
}
}
}
}
function getGroupRowMetadata(item)
{
return {
selectable: false,
focusable: options.groupFocusable,
cssClasses: options.groupCssClass,
columns: {
0: {
colspan: "*",
formatter: options.groupFormatter,
editor: null
}
}
};
}
function getTotalsRowMetadata(item)
{
return {
selectable: false,
focusable: options.totalsFocusable,
cssClasses: options.totalsCssClass,
formatter: options.totalsFormatter,
editor: null
};
}
function getOptions()
{
return options;
}
return {
init,
destroy,
getGroupRowMetadata,
getTotalsRowMetadata,
getOptions
};
}
推荐答案
这更多的是JavaScript而不是打字稿问题。
This is more of a javascript than a typescript question.
在示例1中,您尝试使用类模式,在示例2中,您使用诸如闭包类之类的名称
In example 1 you are trying to use a "class pattern", in example 2 you use something like a "closure class" (theres a name for that pattern which I don't remember).
这两种模式都可以在TS中编写,我个人更喜欢保留闭包类(示例2)。因此,您只需保留代码并添加类型注释即可。启用 strict:true
并为编译器大喊的您具有隐式任何类型提供类型注释。
Both patterns are writeable in TS, and I personally prefer to keep "closure class" (example 2). So, you can just keep your code and add type annotations. Turn on strict: true
and give type annotations to whatever the compiler yells you "has an implicit any type".
个人意见
模式nr.2通常更易于维护(来源?),模式1较难重构,需要更多类型注释,请多加思考,并为此
绑定问题留出空间。您仍然希望在性能要求较高的事物上使用模式1(例如,一个游戏,这似乎不是您的情况),除此之外,模式2是您的最佳选择。即使是经典的OOP案例(扩展类并重写方法),也可以通过选项2(选项袋模式)轻松获得。
Pattern nr.2 is usually more maintanable (sources?), pattern 1 is harder to refactor, requires more type annotations, more thinking and gives you room for the this
binding issue. You'd still want to use pattern 1 on performance intensive things (ex: a game, which seems not to be your case), other than that, pattern 2 is your go. Even classic OOP cases (extend a class and override a method) are easily obtainable through option 2 (option bag pattern).
Typescript的类型系统具有结构性-更为强大而不是经典 java / C#-并且类在TS中不是名义的。这是不使用类的另外两个原因。 class A {}
和 class B {}
或任何具有相同属性的对象都是可以分配的。
Typescript's type system is structural -- quite more powerful than "classic" java/C# -- and classes are not nominal in TS. Those are 2 more reasons not to use classes. class A {}
and class B {}
or any object are assignable if they have the same properties.
编辑:关于此绑定问题
如果您真的要坚持下去。 ..类...
If you really want to stick to ehh... classes...
-
您不能拥有
这个
同时做两件事。如果您的this
是您的课程,则可以通过event.target
找到您的元素。如果您的这个
反弹到一个元素...哦,很好。
You can't have your
this
be 2 things at the same time. If yourthis
is your class, then you can find your element throughevent.target
. If yourthis
was rebound to an element... Oh, well.
所以您会必须调用 element.addEventListener( click,Instance.doSomething.bind(this))
之类的东西。 addEventListener
重新绑定了函数的 this
。 .bind
说:否。
So you'll have to call something like element.addEventListener("click", Instance.doSomething.bind(this))
. addEventListener
rebinds your function's this
. .bind
says: no.
Or element.addEventListener( click ,(... i)=> Instance.doSomething(... i))
如果您的方法是真的是要从另一个 this
上下文中调用,然后编写类似
If your method is really meant to be called from another this
context, then write something like
方法之类的方法(this:HTMLInputElement,x:数字,y:字符串){}
method(this: HTMLInputElement, x: number, y: string) { }
这
是无非就是某种隐藏的函数参数(例如,python和lua明确将其作为第一个参数发送),它被 onX
调用覆盖,这是其中之一JS十亿美元的问题,这是JS类很烂的原因之一。
this
is nothing more than sort of a hidden function parameter (python and lua explicitly send this as a 1st parameter, for instance), which is overriden by the onX
calls, which is one of the JS billion-dollar problems, which is one of the reasons why JS classes suck.
这篇关于如何在类型方法中使用另一个类方法和this上下文?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!