有没有一种方法可以在 JS 模块中启用现代的“this”作用域并消除对“this”的需要。前缀?

问题描述 投票:0回答:2

我有以下模块类,为了使其与非模块功能性 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 可以解决问题,但是对每个变量都这样做会使我的代码变得相当混乱。

一旦他开始构建模块,他就会遇到我上面提到的其他问题。

javascript typescript ecmascript-6 ecmascript-next
2个回答
0
投票

实际上只有两种技术方法,它们都会导致所需的行为。

首先,匿名箭头函数表达式作为处理程序利用保留封闭词法上下文的

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>


0
投票

有没有办法消除这些要求,使模块代码看起来与功能代码完全相同?

是 - 不要使用

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
并实例化它,并将属性放在实例上,那么您将必须使用点语法引用这些属性。没有办法解决这个问题。

© www.soinside.com 2019 - 2024. All rights reserved.