我有以下模块类,为了使其与非模块函数式 javascript 类似,我必须将所有函数绑定到正确的
this
范围,并明确使用 this
作为类成员。
上一个 JavaScript:
var globalVariable = 10;
var button = document.getElementById("button");
button.addEventListener("click", buttonClicked);
function buttonClicked(event) {
var newValue = globalVariable + globalVariable;
alert("Hello world");
}
模块类中的相同内容如下:
export App class {
classVariable = 10;
button = null;
constructor() {
try {
this.bindProperties(App);
this.button = document.getElementById("button");
this.button.addEventListener("click", this.buttonClicked);
}
catch(error) {
this.log(error);
}
}
buttonClicked(event) {
var newValue = this.globalVariable + this.globalVariable;
alert("Hello world");
}
bindProperties(mainClass) {
var properties = Object.getOwnPropertyNames(mainClass.prototype);
for (var key in properties) {
var property = properties[key]
if (property!=="constructor") {
this[property] = this[property].bind(this);
}
}
}
}
我想知道是否有一个设置允许这样做:
export App class {
classVariable = 10;
button = null;
constructor() {
try {
button = document.getElementById("button");
button.addEventListener("click", buttonClicked);
}
catch(error) {
console.log(error);
}
}
buttonClicked(event) {
var newValue = globalVariable + globalVariable;
alert("Hello world");
}
}
基本上,是否有一个选项可以消除添加
this
和绑定 this
类成员的需要。
我正在使用它知道的打字稿,或者默认情况下将
this
添加到代码完整的任何类成员中。如果没有它,它会标记它。
对于新手来说,令人困惑的部分可能是需要将
this
绑定到类模块上。但也许我错过了一些东西。
另外,注意一下,可能是因为上面的绑定方法,如果扩展了子类方法,超类成员不会调用子类成员。
因此,如果调用 baseclass.log() 并且应用程序类扩展了基类,则调用基类方法而不是扩展它的类方法。但这是另一个问题(但相关)。
基本上有两种技术方法,它们都会带来期望的行为。
首先,作为处理程序的箭头函数利用保留封闭词法上下文的
this
值,因此 App
实例化时间为 this
。
// `App` module scope
// a single function statement for handling any `App` instance's
// button-click, based on event and app instance references which
// both get forwarded by an anonymous arrow function expression
// which retains the `this` value of the enclosing lexical context
// at `App` instantiation time.
function handleAppButtonClick(appInstance, evt) {
const computedValue = 2 * appInstance.publicProperty;
console.log({
computedValue, appInstance, button: evt.currentTarget,
});
}
/* export */class App {
#privateButton;
publicProperty;
constructor(buttonSelector = '#button', value = 10) {
value = parseInt(value, 10);
this.publicProperty = isFinite(value) ? value : 10;
this.#privateButton =
document.querySelector(buttonSelector);
this.#privateButton.addEventListener(
// - an arrow function as handler makes use of retaining
// the `this` value of the enclosing lexical context,
// hence the `App` instantiation time's `this`.
"click", evt => handleAppButtonClick(this, evt)
);
}
// no need for any prototypal implemented event handler.
}
// end ... App module scope
// other module's scope
// import App from '...'
new App('button:first-child');
new App('button:nth-child(2)', 20);
.as-console-wrapper { max-height: 90%!important; }
<button type="button">1st test logs ... 20</button>
<button type="button">2nd test logs ... 40</button>
其次,用于处理任何
App
实例的按钮单击的单个函数语句。它是创建处理程序函数的基础,该函数显式地将 App
实例绑定为创建的处理程序的 this
上下文。
// `App` module scope
// a single function statement for handling any App-instance's
// button-click. It is the base for creating handler-functions
// which explicitly do bind an `App` instance as the created
// handler's `this` context.
function handleButtonClickOfBoundAppInstance(evt) {
const computedValue = 2 * this.publicProperty;
console.log({
computedValue, appInstance: this, button: evt.currentTarget,
});
}
/* export */class App {
#privateButton;
publicProperty;
constructor(buttonSelector = '#button', value = 10) {
value = parseInt(value, 10);
this.publicProperty = isFinite(value) ? value : 10;
this.#privateButton =
document.querySelector(buttonSelector);
this.#privateButton.addEventListener(
// - explicitly bind an `App` instance as
// the created handler's `this` context.
"click", handleButtonClickOfBoundAppInstance.bind(this)
);
}
// no need for any prototypal implemented event handler.
}
// end ... App module scope
// other module's scope
// import App from '...'
new App('button:first-child');
new App('button:nth-child(2)', 20);
.as-console-wrapper { max-height: 90%!important; }
<button type="button">1st test logs ... 20</button>
<button type="button">2nd test logs ... 40</button>