我有以下模块类,为了使其与非模块功能性 javascript 类似,我必须调用一个函数将所有类成员函数绑定到正确的
this
范围,并且在引用时必须显式添加 this.
班级成员。
有没有办法消除这些要求,使模块代码看起来与功能代码完全相同(看看它在nodejs中的typescript中如何工作)。
代码示例中唯一需要注意的是添加了
this.
以及需要在模块类中调用bindProperties()
函数。如果您不调用 bindProperties()
函数,则变量、类和事件处理程序都会失去作用域。在这些情况下,this
可以是任何东西。示例代码的内容并不重要。
在 HTML 页面中包含 javascript(请注意,在任何代码或事件处理程序中都没有提及
this
):
<script src="myscript.js"></script>
// myscript.js
"use strict"
var button = document.getElementById("button");
button.addEventListener("click", buttonClicked);
function buttonClicked(event) {
alert("Hello world");
}
模块类中的相同内容如下(请注意
this.
的多次使用以及构造函数中对 bind()
的必要调用):
<script src="myscript.js" type="module"></script>
// myscript.js
export App class {
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) {
alert("Hello world");
}
// this method is required to make the `this.` point to the class in all of the functions and event handlers
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);
}
}
}
}
我想知道的是是否有一个设置可以消除需要写
this.
并消除需要调用bindProperties()
。注意缺少 this.
和缺少 bindProperties()
:
<script src="myscript.js" type="module" version="2"></script>
// myscript.js
export App class {
button = null;
constructor() {
try {
button = document.getElementById("button");
button.addEventListener("click", buttonClicked);
}
catch(error) {
console.log(error);
}
}
buttonClicked(event) {
alert("Hello world");
}
}
基本上,是否有一个选项可以消除添加
this
的需要,以及是否有一个选项可以消除调用 bindProperties 的需要。
我在说什么?看看nodejs中的typescript/javascript。你永远不需要使用
this.
,你不需要在类上调用绑定函数。范围始终是您期望的范围。
但是对于客户端 JavaScript,我使用打字稿,它知道范围问题,并且默认情况下它会将
this.
添加到代码完整的任何类成员中。如果没有它,它会标记它。
这会让 Javascript 新手感到困惑,他们可能不明白为什么需要在类模块上绑定
this
,而不必在非模块脚本或 Nodejs 中这样做。
但也许我错过了一些东西。我希望这是有道理的。
同样,如果您在网页中包含的普通脚本中编写代码,则无需执行使用模块时必须执行的任何范围内的操作。
换句话说,是否有一个设置可以使模块代码和行为在语法上与非模块/nodejs代码相同?答案可能显然不是,但打字稿有很多选项,也许我错过了。
更新
在这个链接的post中是这样的评论:
将 myVariable 更改为 this.myVariable 可以解决问题,但是对每个变量都这样做会使我的代码变得相当混乱。
一旦他开始构建模块,他就会遇到我上面提到的其他问题。
实际上只有两种技术方法,它们都会导致所需的行为。
首先,匿名箭头函数表达式作为处理程序利用保留封闭词法上下文的
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: 82%!important; }
<button type="button">1st test logs ... 20 ... as computed value</button>
<button type="button">2nd test logs ... 40 ... as computed value</button>
其次,用于处理任何
App
实例的按钮单击的单个函数语句。它是创建处理程序函数的基础,该函数显式执行 bind
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: 82%!important; }
<button type="button">1st test logs ... 20 ... as computed value</button>
<button type="button">2nd test logs ... 40 ... as computed value</button>
有没有办法消除这些要求,使模块代码看起来与功能代码完全相同?
是 - 不要使用
class
。使用 function
表示功能代码:
function myscript() {
const button = document.getElementById("button");
button.addEventListener("click", buttonClicked);
function buttonClicked(event) {
alert("Hello world");
}
}
或者将代码放入 ES 模块中:
<script src="myscript.mjs"></script>
// myscript.mjs
const button = document.getElementById("button");
button.addEventListener("click", buttonClicked);
function buttonClicked(event) {
alert("Hello world");
}
如果您要定义
class App
并实例化它,并将属性放在实例上,那么您将必须使用点语法引用这些属性。没有办法解决这个问题。